spring多個事務(wù)管理器踩坑及解決
多個事務(wù)管理器踩坑
在項目中使用了兩個事務(wù)管理器,因為項目中設(shè)計到兩個數(shù)據(jù)庫的操作,所以就聲明了兩個事務(wù)管理器;
但是在使用@Transactional注解的時候,沒有手動指定事務(wù)要使用哪個,就有可能會導(dǎo)致事務(wù)不生效
案例
1、在項目中,我用到了A和B兩個數(shù)據(jù)庫,所以,此時就需要聲明兩個事務(wù)管理器,我們假設(shè)命名為:ATransactionManager和BTransactionManager(這兩個事務(wù)管理器,分別對應(yīng)著A和B兩個數(shù)據(jù)庫就)
2、在使用的時候,如果當前方法中,都是對A數(shù)據(jù)庫的表進行操作,那就需要在@Transactional注解的value屬性上指定ATransactional
這里的aDataSource和bDataSource都是根據(jù)a和b兩個數(shù)據(jù)庫生成的,只需要在配置類中,加上這幾行代碼,spring在啟動的時候,就會把這兩個事務(wù)管理器放入到spring容器中
@Bean public PlatformTransactionManager aTransactionManager() { return new DataSourceTransactionManager(aDataSource()); } @Bean public PlatformTransactionManager bTransactionManager() { return new DataSourceTransactionManager(bDataSource()); }
配置就完成了,這里,就和添加一個普通的bean對象是一樣的,都會放到spring容器中
我遇到的問題是
我在一個方法上加了事務(wù)注解,但是沒有指定value,實際上,這個接口中是要去操作a數(shù)據(jù)庫的三張表,但是發(fā)現(xiàn)接口中事務(wù)回滾的時候,并沒有把其他三張表的數(shù)據(jù)都回滾,debug看代碼,也確實去執(zhí)行了回滾的操作,最后發(fā)現(xiàn)是沒有指定aTransactionManager的原因
源碼解析
我們都知道,事務(wù)的源碼,在
org.springframework.transaction.interceptor.TransactionInterceptor#invoke ?? ?org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction ?? ? final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); //根據(jù)事務(wù)屬性獲取獲取事務(wù)管理器,這里一般是 DataSourceTransactionManager,我們也可以自己去指定事務(wù)管理器 final PlatformTransactionManager tm = determineTransactionManager(txAttr);?? ?
在這個方法中,有兩行代碼,是需要關(guān)注的,上面一行,是根據(jù)當前的方法名,獲取對應(yīng)的事務(wù)注解信息,這里有一個前置的知識點,需要說明:在spring初始化的過程中,會對方法或者類上對的事務(wù)注解進行解析,解析之后,會存到內(nèi)存中,其中
org.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(org.springframework.core.annotation.AnnotationAttributes)
在這個方法中,會解析@Transactional注解,并將解析到的配置信息,存儲到RuleBasedTransactionAttribute這個bean中,存儲到這個bean的qualifier字段上,所以,事務(wù)攔截器在解析的時候,是根據(jù)這個qualifier字段的value,來獲取事務(wù)管理器的,這是一個前提知識,需要知道
determineTransactionManager()
我們著重來看這個方法:
/** * 這個方法,是來獲取事務(wù)管理器的,如果我們有自己指定,就可以在使用@Transactional注解的時候,通過value,指定我們要使用的事務(wù)管理器 * Determine the specific transaction manager to use for the given transaction. */ @Nullable protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) { // Do not attempt to lookup tx manager if no tx attributes are set if (txAttr == null || this.beanFactory == null) { return getTransactionManager(); } // 1.由于在解析事務(wù)注解的時候,將value,放到了qualifier屬性上,所以要這樣獲取,我們只需要知道,這里的qualifier就是事務(wù)注解中的value對應(yīng)的值 String qualifier = txAttr.getQualifier(); // 2.如果在@Transaction的value指定事務(wù)管理器,會執(zhí)行這里,其實就是從spring容器中獲取指定的事務(wù)管理器 if (StringUtils.hasText(qualifier)) { return determineQualifiedTransactionManager(this.beanFactory, qualifier); } // 3. else if (StringUtils.hasText(this.transactionManagerBeanName)) { return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName); } else { // 4.先判斷是否有指定事務(wù)管理器,如果沒有,就獲取默認的 PlatformTransactionManager defaultTransactionManager = getTransactionManager(); if (defaultTransactionManager == null) { // 獲取默認的事務(wù)管理器 defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY); if (defaultTransactionManager == null) { //由于我們沒有指定,會從spring容器中按照類型獲取,所以我們只需要往spring容器中注入一個事務(wù)管理器即可,springboot應(yīng)用會默認注入一個事務(wù)管理器 defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class); this.transactionManagerCache.putIfAbsent( DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager); } } return defaultTransactionManager; } }
這個方法,我認為,就是這篇博客所有的內(nèi)容了,注釋中的第一點和第二點我搞懂了,第二點如果點進去,就會發(fā)現(xiàn),實際上,調(diào)用的就是beanFactory.getBean(name,type)這個方法,就是根據(jù)我們指定的事務(wù)管理器,從spring容器中獲取我們想要使用的事務(wù)管理器
對于第三點,我還沒有搞懂,只是看到是在org.springframework.transaction.interceptor.TransactionInterceptor#readObject中賦的值,所以先暫時跳過
對于第四點:其實簡單來說,就是我們沒有指定要使用的事務(wù)管理器,spring會幫助我們?nèi)ギ斍皊pring容器中,根據(jù)類型(PlatformTransactionManager.class)獲取一個默認的事務(wù)管理器,對于springboot應(yīng)用來說,無需關(guān)心這一點,因為springboot幫助我們自動注入了一個事務(wù)管理器:DataSourceTransactionManager
所以,上面這個方法,就解釋了,為什么,我在沒有指定事務(wù)管理器的情況下,事務(wù)未生效
springboot自動注入的事務(wù)管理器
在spring自動注入的配置中,幫我們自動注入了這個一個類
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
@Configuration @ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class }) @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) @EnableConfigurationProperties(DataSourceProperties.class) public class DataSourceTransactionManagerAutoConfiguration { @Configuration @ConditionalOnSingleCandidate(DataSource.class) static class DataSourceTransactionManagerConfiguration { private final DataSource dataSource; private final TransactionManagerCustomizers transactionManagerCustomizers; DataSourceTransactionManagerConfiguration(DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { this.dataSource = dataSource; this.transactionManagerCustomizers = transactionManagerCustomizers .getIfAvailable(); } @Bean @ConditionalOnMissingBean(PlatformTransactionManager.class) public DataSourceTransactionManager transactionManager( DataSourceProperties properties) { // 根據(jù)DataSource,初始化一個DataSourceTransactionManager對象,然后return;所以,這里就會把初始化的事務(wù)管理器放到spring容器中 DataSourceTransactionManager transactionManager = new DataSourceTransactionManager( this.dataSource); if (this.transactionManagerCustomizers != null) { this.transactionManagerCustomizers.customize(transactionManager); } return transactionManager; } } }
在這個bean中,就注入了一個事務(wù)管理器,但是有一個前提,就是@ConditionalOnSingleCandidate(DataSource.class),這個注解,百度了一下,大致的意思就是說如果當前容器中只有一個DataSource,才會執(zhí)行下面注入的動作
所以,對于springboot應(yīng)用,如果我們在項目中,只有一個DataSource,那spring會幫我們自動注入一個,在使用事務(wù)注解的時候,也就不需要去指定value了
未確認點
這里就是有一個點不太明白,在有多個數(shù)據(jù)源的情況下,springboot就不幫我們注入事務(wù)管理器了,但是我在debug的時候,發(fā)現(xiàn),還是取的默認的事務(wù)管理器,也就是DataSourceTransactionalManager, 這個點沒太搞懂原因,因為感覺和源碼中自己理解的不太一樣
結(jié)論
總之,在項目中有多個數(shù)據(jù)源的時候,在事務(wù)注解上,最好標明使用哪個事務(wù)管理器
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
JAVA中通過自定義注解進行數(shù)據(jù)驗證的方法
java 自定義注解驗證可自己添加所需要的注解,下面這篇文章主要給大家介紹了關(guān)于JAVA中通過自定義注解進行數(shù)據(jù)驗證的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08java中l(wèi)ist使用時需避免的場景總結(jié)
眾所周知,Java為開發(fā)者提供了多種集合類的實現(xiàn),其中幾乎所有業(yè)務(wù)代碼都需要用到List,但List的錯誤使用也會導(dǎo)致諸多問題,所以本文我們就來看一看幾個錯誤使用List的場景吧2023-10-10