项目中使用了多数据源配置,在添加事务的时候,出现了如下异常
No qualifying bean of type 'org.springframework.transaction.TransactionManager' available
这个是因为找不到对应的事务管理器报出的错误.网上有很多种方法,其实已经说的很明确了.
就是给自己配置的数据源,分别添加各自的事务管理,然后在使用事务的地方注明自己的事务管理
数据源配置时,添加自己的事务管理
@Bean(name = "middleDataSourceDruid")
@ConfigurationProperties("spring.datasource.middle-druid")
public DataSource middleDataSourceDruid() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "midDataSourceTransactionManager")
public PlatformTransactionManager midDataSourceTransactionManager(@Qualifier("middleDataSourceDruid") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
使用事务时,通过value指定事务管理器
@Transactional(rollbackFor = Exception.class,value = "midDataSourceTransactionManager")
这样就可以解决这个报错.
但有一些特殊情况,由于我在项目中使用的是mybatis-plus.调用公共方法saveBatch()时,还是会报错,说找不到事务管理器.(有时候也报找到多个)但是调用save()方法就不会有这个问题.这个就很尴尬了.在网上找了找.没有找到解决办法.只能自己去跟源码找了
事务管理器的选择主要看
TransactionAspectSupport 类下的这个方法determineTransactionManager
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
跟进去可以发现
// Do not attempt to lookup tx manager if no tx attributes are set
if (txAttr == null || this.beanFactory == null) {
return getTransactionManager();
}
String qualifier = txAttr.getQualifier();
if (StringUtils.hasText(qualifier)) {
return determineQualifiedTransactionManager(this.beanFactory, qualifier);
}
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
}
else {
TransactionManager defaultTransactionManager = getTransactionManager();
if (defaultTransactionManager == null) {
defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
if (defaultTransactionManager == null) {
defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
return defaultTransactionManager;
}
有这么一行代码
String qualifier = txAttr.getQualifier();
这里是获取@Transactional 中的value值,然后通过值去获取对应的事务管理器.如果为空值,就会去获取默认的事务管理器.但我再在配置多数据源的事务管理器时,没有告诉spring,谁才是默认的,然后就会报错No qualifying bean of type 'org.springframework.transaction.TransactionManager',有的方法就是直接在配置事务管理器时,添加@Primary,来指定默认事务管理器.
@Bean(name = "midDataSourceTransactionManager")
@Primary
public PlatformTransactionManager midDataSourceTransactionManager(@Qualifier("middleDataSourceDruid") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
但我每个事务都指定了由谁来处理事务,为什么还会去找默认的事务管理器呢?这就让我很不明白,而且跟中代码,发现qualifier的值也是对的,也找到了事务管理器.但还是报错.有点抓狂.
正在焦急中,我突然发现,在调用saveBatch()方法时,String qualifier = txAttr.getQualifier();这行的断点进去了两次,在第二次的时候,值为 "" (空字符串),也就是没有指定事务管理器,然后去找默认的,就报错了.然后进入saveBatch()方法一看,才明白,原来saveBatch()方法上还有一个@Transactional,且value没有赋值.
@Transactional(
rollbackFor = {Exception.class}
)
default boolean saveBatch(Collection<T> entityList) {
return this.saveBatch(entityList, 1000);
}
找到原因了,因为是公共方法,所以不会指定事务管理器,使用的是默认管理器,但我在代码中没有告诉spring哪个数据源的事务管理器才是默认的,所以就一直在报错.弄明白了这层原因,那就方便了,就是添加一个默认事务管理就可以了.
使用第三方jar包方便是方便了.但一定要弄明白你调用的方法是如何去运行的,不然就会像我一样,为一个本来很明显的问题困扰很久很久.