当使用 @Transactional 注解时,可能会遇到多数据源的异常问题。本篇攻略将会详细讲解这个问题的根本原因并且提供两个示例来说明。
1. 什么是多数据源
多数据源即指一个系统维护了多个数据库,每个数据库可能拥有不同的表或者对象。在应用程序中,连接各个数据库的连接信息通常是不同的。
2. 问题描述
当使用 @Transactional 注解时,会抛出异常,异常信息如下所示:
org.springframework.transaction.TransactionSystemException: Could not commit JDBC transaction; nested exception is java.sql.SQLException: Unable to commit against JDBC Connection
...
Caused by: java.sql.SQLException: Unable to commit against JDBC Connection
...
3. 解决方法
在多数据源的环境中,由于存在多个连接,因此,从一个连接(如主数据源)中启动的事务可能无法在其他连接(如从数据源)中进行提交,从而导致错误。要解决这个问题,可以使用两种方法:
3.1 在 @Transactional 注解中指定事务管理器
使用 @Transactional 注解的时候,可以通过指定数据源的方式来实现,代码示例如下:
@Service
public class UserService {
@Autowired
@Qualifier("orderTransactionManager")
private PlatformTransactionManager orderTransactionManager;
@Autowired
@Qualifier("memberTransactionManager")
private PlatformTransactionManager memberTransactionManager;
@Transactional(transactionManager = "orderTransactionManager")
public void doSomething1() {
//do something in order database
}
@Transactional(transactionManager = "memberTransactionManager")
public void doSomething2() {
//do something in member database
}
}
3.2 使用 ChainedTransactionManager 转发事务
使用 ChainedTransactionManager 来转发事务是另一种常见的方法。它是一个特殊的 PlatformTransactionManager,它将多个 PlatformTransactionManager 组合成一个事务管理器。
ChainedTransactionManager 与其他 Spring 的事务管理器一样,它可以像其他 TransactionManager 一样进行调用,只是需要同时提交和回滚各个 TransactionManager。
下面是示例代码:
@Configuration
@Table
@EnableTransactionManagement
@SpringBootApplication
public class SpringMultiDatasourceDemoApplication {
@Autowired
private DataSource orderDataSource;
@Autowired
private DataSource memberDataSource;
@Bean(name = "orderTransactionManager")
public PlatformTransactionManager orderTransactionManager() {
return new DataSourceTransactionManager(orderDataSource);
}
@Bean(name = "memberTransactionManager")
public PlatformTransactionManager memberTransactionManager() {
return new DataSourceTransactionManager(memberDataSource);
}
@Bean
public PlatformTransactionManager txManager() {
List<PlatformTransactionManager> tms = new ArrayList<>(2);
tms.add(orderTransactionManager());
tms.add(memberTransactionManager());
return new ChainedTransactionManager(tms);
}
public static void main(String[] args) {
SpringApplication.run(SpringMultiDatasourceDemoApplication.class, args);
}
}
4. 示例说明
4.1 第一个示例
第一个示例中,我们有两个数据源:order 和 member,它们都具有一个名为 User 的表,我们要使用 @Transactional 注解执行事务,对两个表进行操作。
代码示例:
@Service
public class UserService {
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
@Autowired
public UserService(OrderRepository orderRepository, MemberRepository memberRepository) {
this.orderRepository = orderRepository;
this.memberRepository = memberRepository;
}
@Transactional
public void save(User user) {
orderRepository.save(user);
memberRepository.save(user);
}
}
当使用上面的代码时,会因为多数据源而出现异常,导致事务无法提交。我们可以使用方法 1 中的方式来指定数据源,代码示例如下:
@Service
public class UserService {
@Autowired
@Qualifier("orderTransactionManager")
private PlatformTransactionManager orderTransactionManager;
@Autowired
@Qualifier("memberTransactionManager")
private PlatformTransactionManager memberTransactionManager;
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
@Autowired
public UserService(OrderRepository orderRepository, MemberRepository memberRepository) {
this.orderRepository = orderRepository;
this.memberRepository = memberRepository;
}
@Transactional(transactionManager = "orderTransactionManager")
public void save(User user) {
orderRepository.save(user);
memberRepository.save(user);
}
}
4.2 第二个示例
第二个示例中,我们有两个数据源:order 和 member,它们都具有一个名为 User 的表,我们要使用 @Transactional 注解执行事务,对两个表进行操作。
代码示例:
@Service
public class UserService {
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
@Autowired
public UserService(OrderRepository orderRepository, MemberRepository memberRepository) {
this.orderRepository = orderRepository;
this.memberRepository = memberRepository;
}
@Transactional
public void save(User user) {
orderRepository.save(user);
memberRepository.save(user);
}
}
当使用上面的代码时,会因为多数据源而出现异常,导致事务无法提交。我们可以使用方法 2 中的方式来转发事务,代码示例如下:
@Configuration
@Table
@EnableTransactionManagement
@SpringBootApplication
public class SpringMultiDatasourceDemoApplication {
@Autowired
private DataSource orderDataSource;
@Autowired
private DataSource memberDataSource;
@Bean(name = "orderTransactionManager")
public PlatformTransactionManager orderTransactionManager() {
return new DataSourceTransactionManager(orderDataSource);
}
@Bean(name = "memberTransactionManager")
public PlatformTransactionManager memberTransactionManager() {
return new DataSourceTransactionManager(memberDataSource);
}
@Bean
public PlatformTransactionManager txManager() {
List<PlatformTransactionManager> tms = new ArrayList<>(2);
tms.add(orderTransactionManager());
tms.add(memberTransactionManager());
return new ChainedTransactionManager(tms);
}
public static void main(String[] args) {
SpringApplication.run(SpringMultiDatasourceDemoApplication.class, args);
}
}
5. 总结
本篇攻略详细介绍了解决多数据源下 @Transactional 注解异常的两种方法,分别为指定事务管理器和使用 ChainedTransactionManager 转发事务。通过两个示例,我们可以发现,无论哪种方法,都可以解决在多数据源环境下的事务异常问题。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:@Transactional注解异常报错之多数据源详解 - Python技术站