欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

spring多個(gè)事務(wù)管理器踩坑及解決

 更新時(shí)間:2022年11月21日 10:23:58   作者:小小少年_  
這篇文章主要介紹了spring多個(gè)事務(wù)管理器踩坑及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

多個(gè)事務(wù)管理器踩坑

在項(xiàng)目中使用了兩個(gè)事務(wù)管理器,因?yàn)轫?xiàng)目中設(shè)計(jì)到兩個(gè)數(shù)據(jù)庫的操作,所以就聲明了兩個(gè)事務(wù)管理器;

但是在使用@Transactional注解的時(shí)候,沒有手動(dòng)指定事務(wù)要使用哪個(gè),就有可能會(huì)導(dǎo)致事務(wù)不生效

案例

1、在項(xiàng)目中,我用到了A和B兩個(gè)數(shù)據(jù)庫,所以,此時(shí)就需要聲明兩個(gè)事務(wù)管理器,我們假設(shè)命名為:ATransactionManager和BTransactionManager(這兩個(gè)事務(wù)管理器,分別對應(yīng)著A和B兩個(gè)數(shù)據(jù)庫就)

2、在使用的時(shí)候,如果當(dāng)前方法中,都是對A數(shù)據(jù)庫的表進(jìn)行操作,那就需要在@Transactional注解的value屬性上指定ATransactional

這里的aDataSource和bDataSource都是根據(jù)a和b兩個(gè)數(shù)據(jù)庫生成的,只需要在配置類中,加上這幾行代碼,spring在啟動(dòng)的時(shí)候,就會(huì)把這兩個(gè)事務(wù)管理器放入到spring容器中

@Bean
public PlatformTransactionManager aTransactionManager() {
    return new DataSourceTransactionManager(aDataSource());
}

@Bean
public PlatformTransactionManager bTransactionManager() {
    return new DataSourceTransactionManager(bDataSource());
}

配置就完成了,這里,就和添加一個(gè)普通的bean對象是一樣的,都會(huì)放到spring容器中

我遇到的問題是

我在一個(gè)方法上加了事務(wù)注解,但是沒有指定value,實(shí)際上,這個(gè)接口中是要去操作a數(shù)據(jù)庫的三張表,但是發(fā)現(xiàn)接口中事務(wù)回滾的時(shí)候,并沒有把其他三張表的數(shù)據(jù)都回滾,debug看代碼,也確實(shí)去執(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);?? ?

在這個(gè)方法中,有兩行代碼,是需要關(guān)注的,上面一行,是根據(jù)當(dāng)前的方法名,獲取對應(yīng)的事務(wù)注解信息,這里有一個(gè)前置的知識(shí)點(diǎn),需要說明:在spring初始化的過程中,會(huì)對方法或者類上對的事務(wù)注解進(jìn)行解析,解析之后,會(huì)存到內(nèi)存中,其中

org.springframework.transaction.annotation.SpringTransactionAnnotationParser#parseTransactionAnnotation(org.springframework.core.annotation.AnnotationAttributes)

在這個(gè)方法中,會(huì)解析@Transactional注解,并將解析到的配置信息,存儲(chǔ)到RuleBasedTransactionAttribute這個(gè)bean中,存儲(chǔ)到這個(gè)bean的qualifier字段上,所以,事務(wù)攔截器在解析的時(shí)候,是根據(jù)這個(gè)qualifier字段的value,來獲取事務(wù)管理器的,這是一個(gè)前提知識(shí),需要知道

determineTransactionManager()

我們著重來看這個(gè)方法:

/**
 * 這個(gè)方法,是來獲取事務(wù)管理器的,如果我們有自己指定,就可以在使用@Transactional注解的時(shí)候,通過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ù)注解的時(shí)候,將value,放到了qualifier屬性上,所以要這樣獲取,我們只需要知道,這里的qualifier就是事務(wù)注解中的value對應(yīng)的值
	String qualifier = txAttr.getQualifier();
	// 2.如果在@Transaction的value指定事務(wù)管理器,會(huì)執(zhí)行這里,其實(shí)就是從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ù)管理器,如果沒有,就獲取默認(rèn)的
		PlatformTransactionManager defaultTransactionManager = getTransactionManager();
		if (defaultTransactionManager == null) {
			// 獲取默認(rèn)的事務(wù)管理器
			defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
			if (defaultTransactionManager == null) {
				//由于我們沒有指定,會(huì)從spring容器中按照類型獲取,所以我們只需要往spring容器中注入一個(gè)事務(wù)管理器即可,springboot應(yīng)用會(huì)默認(rèn)注入一個(gè)事務(wù)管理器
				defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
				this.transactionManagerCache.putIfAbsent(
						DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
			}
		}
		return defaultTransactionManager;
	}
}

這個(gè)方法,我認(rèn)為,就是這篇博客所有的內(nèi)容了,注釋中的第一點(diǎn)和第二點(diǎn)我搞懂了,第二點(diǎn)如果點(diǎn)進(jìn)去,就會(huì)發(fā)現(xiàn),實(shí)際上,調(diào)用的就是beanFactory.getBean(name,type)這個(gè)方法,就是根據(jù)我們指定的事務(wù)管理器,從spring容器中獲取我們想要使用的事務(wù)管理器

對于第三點(diǎn),我還沒有搞懂,只是看到是在org.springframework.transaction.interceptor.TransactionInterceptor#readObject中賦的值,所以先暫時(shí)跳過

對于第四點(diǎn):其實(shí)簡單來說,就是我們沒有指定要使用的事務(wù)管理器,spring會(huì)幫助我們?nèi)ギ?dāng)前spring容器中,根據(jù)類型(PlatformTransactionManager.class)獲取一個(gè)默認(rèn)的事務(wù)管理器,對于springboot應(yīng)用來說,無需關(guān)心這一點(diǎn),因?yàn)閟pringboot幫助我們自動(dòng)注入了一個(gè)事務(wù)管理器:DataSourceTransactionManager

所以,上面這個(gè)方法,就解釋了,為什么,我在沒有指定事務(wù)管理器的情況下,事務(wù)未生效

springboot自動(dòng)注入的事務(wù)管理器

在spring自動(dòng)注入的配置中,幫我們自動(dòng)注入了這個(gè)一個(gè)類

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,初始化一個(gè)DataSourceTransactionManager對象,然后return;所以,這里就會(huì)把初始化的事務(wù)管理器放到spring容器中
			DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
					this.dataSource);
			if (this.transactionManagerCustomizers != null) {
				this.transactionManagerCustomizers.customize(transactionManager);
			}
			return transactionManager;
		}
	}
}

在這個(gè)bean中,就注入了一個(gè)事務(wù)管理器,但是有一個(gè)前提,就是@ConditionalOnSingleCandidate(DataSource.class),這個(gè)注解,百度了一下,大致的意思就是說如果當(dāng)前容器中只有一個(gè)DataSource,才會(huì)執(zhí)行下面注入的動(dòng)作

所以,對于springboot應(yīng)用,如果我們在項(xiàng)目中,只有一個(gè)DataSource,那spring會(huì)幫我們自動(dòng)注入一個(gè),在使用事務(wù)注解的時(shí)候,也就不需要去指定value了

未確認(rèn)點(diǎn)

這里就是有一個(gè)點(diǎn)不太明白,在有多個(gè)數(shù)據(jù)源的情況下,springboot就不幫我們注入事務(wù)管理器了,但是我在debug的時(shí)候,發(fā)現(xiàn),還是取的默認(rèn)的事務(wù)管理器,也就是DataSourceTransactionalManager, 這個(gè)點(diǎn)沒太搞懂原因,因?yàn)楦杏X和源碼中自己理解的不太一樣

結(jié)論

總之,在項(xiàng)目中有多個(gè)數(shù)據(jù)源的時(shí)候,在事務(wù)注解上,最好標(biāo)明使用哪個(gè)事務(wù)管理器

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java多線程之線程安全問題詳情

    Java多線程之線程安全問題詳情

    這篇文章主要介紹了Java多線程之線程安全問題詳情,線程安全問題是指因多線程搶占式執(zhí)行而導(dǎo)致程序出現(xiàn)bug的問題。內(nèi)容介紹詳細(xì)內(nèi)容需要的小伙伴可以參考下面文章內(nèi)容
    2022-06-06
  • SpringBoot處理接口冪等性的兩種方法詳解

    SpringBoot處理接口冪等性的兩種方法詳解

    接口冪等性處理算是一個(gè)非常常見的需求了,我們在很多項(xiàng)目中其實(shí)都會(huì)遇到。本文為大家總結(jié)了兩個(gè)處理接口冪等性的兩種常見方案,需要的可以參考一下
    2022-06-06
  • 基于Java實(shí)現(xiàn)QQ郵箱發(fā)送工具類

    基于Java實(shí)現(xiàn)QQ郵箱發(fā)送工具類

    我們在日常開發(fā)中,需要實(shí)現(xiàn)一個(gè)對郵箱的發(fā)送,今天就實(shí)現(xiàn)郵箱的發(fā)送工具類,只需要一些注冊郵箱之后的配置即可,感興趣的小伙伴可以了解下
    2023-12-12
  • JAVA中通過自定義注解進(jìn)行數(shù)據(jù)驗(yàn)證的方法

    JAVA中通過自定義注解進(jìn)行數(shù)據(jù)驗(yàn)證的方法

    java 自定義注解驗(yàn)證可自己添加所需要的注解,下面這篇文章主要給大家介紹了關(guān)于JAVA中通過自定義注解進(jìn)行數(shù)據(jù)驗(yàn)證的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • mybatis-plus支持null字段全量更新的兩種方法

    mybatis-plus支持null字段全量更新的兩種方法

    本文主要介紹了mybatis-plus支持null字段全量更新的兩種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • MyBatis?Mapper映射器的具體用法

    MyBatis?Mapper映射器的具體用法

    映射器是MyBatis中最重要的文件,映射器由Java接口和XML文件共同組成,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • java實(shí)現(xiàn)多選批量刪除功能

    java實(shí)現(xiàn)多選批量刪除功能

    工作中批量刪除可以提高我們的工作效率,今天這篇文章主要介紹了java實(shí)現(xiàn)多選批量刪除功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • java中l(wèi)ist使用時(shí)需避免的場景總結(jié)

    java中l(wèi)ist使用時(shí)需避免的場景總結(jié)

    眾所周知,Java為開發(fā)者提供了多種集合類的實(shí)現(xiàn),其中幾乎所有業(yè)務(wù)代碼都需要用到List,但List的錯(cuò)誤使用也會(huì)導(dǎo)致諸多問題,所以本文我們就來看一看幾個(gè)錯(cuò)誤使用List的場景吧
    2023-10-10
  • Java中的二維數(shù)組的賦值與輸出方式

    Java中的二維數(shù)組的賦值與輸出方式

    這篇文章主要介紹了Java中的二維數(shù)組的賦值與輸出方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • spring boot 全局異常處理方法匯總

    spring boot 全局異常處理方法匯總

    這篇文章主要介紹了spring boot 全局異常處理方法匯總,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10

最新評論