您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Spring中@Transactional事務(wù)不生效如何解決,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
事務(wù)管理在系統(tǒng)開發(fā)中是不可缺少的一部分,Spring
提供了很好事務(wù)管理機(jī)制,主要分為編程式事務(wù)
和聲明式事務(wù)
兩種。
編碼式事務(wù)管理:將事務(wù)控制代碼編寫在業(yè)務(wù)代碼之中。
聲明式事務(wù)管理:基于AOP(面向切面編程),事務(wù)管理與業(yè)務(wù)邏輯解耦。兩種實(shí)現(xiàn):(1)在配置文件(xml)中配置。(2)基于@Transactional注解。
@Transactional 可以作用在接口
、類
、類方法
。
作用于類:當(dāng)把@Transactional 注解放在類上時,表示所有該類的public
方法都配置相同的事務(wù)屬性信息。
作用于方法:當(dāng)類配置了@Transactional,方法也配置了@Transactional,方法的事務(wù)會覆蓋類的事務(wù)配置信息。
作用于接口:不推薦這種使用方法,因?yàn)橐坏?biāo)注在Interface上并且配置了Spring AOP 使用CGLib動態(tài)代理,將會導(dǎo)致@Transactional注解失效
MySql 的 MyISAM 引擎不支持回滾,如果需要自動回滾事務(wù),需要將MySql的引擎設(shè)置成InnoDB;
//@Transactional注解在private方法上會失效@Transactionalprivate void deleteUser() throws MyException{userMapper.deleteUserA();int i = 1/0;userMapper.deleteUserB();}
idea直接會給出提示Methods annotated with ‘@Transactional’ must be overridable ,原理很簡單,private修飾的方式,spring無法生成動態(tài)代理,AOP代理分別在intercept()和invoke()方法判斷是否進(jìn)行事務(wù)攔截,這兩個方法都會間接調(diào)用AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法來獲取事務(wù)控制的相關(guān)屬性。這其中有以下一段代碼
/** * Same signature as {@link #getTransactionAttribute}, but doesn't cache the result. * {@link #getTransactionAttribute} is effectively a caching decorator for this method. * <p>As of 4.1.8, this method can be overridden. * @since 4.1.8 * @see #getTransactionAttribute */ protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}//... }
這段代碼會導(dǎo)致no-public的方法無法進(jìn)入事務(wù)控制,所以一定要確保自己需要進(jìn)行事務(wù)控制的方法包含public修飾符。
當(dāng)異常被捕獲后,并且沒有再拋出,那么deleteUserA是不會回滾的,例如:
@Transactionalpublic void deleteUser() {userMapper.deleteUserA();try {int i = 1 / 0;userMapper.deleteUserB();} catch (Exception e) {e.printStackTrace();}}
異步雖然拋出了,但是拋出的是非RuntimeException類型的異常,依舊不會生效,例如:
@Transactionalpublic void deleteUser() throws MyException{userMapper.deleteUserA();try {int i = 1 / 0;userMapper.deleteUserB();} catch (Exception e) {throw new MyException();}}
注解為事務(wù)范圍的方法中,事務(wù)的回滾僅僅對于unchecked的異常有效。對于checked異常無效。也就是說事務(wù)回滾僅僅發(fā)生在,出現(xiàn)RuntimeException或Error的時候。通俗一點(diǎn)就是:代碼中出現(xiàn)的空指針等異常,會被回滾。而文件讀寫、網(wǎng)絡(luò)超時問題等,spring就沒法回滾了。
解決方案:如果指定了回滾異常類型為Exception,那么就可以回滾Checked類型異常了。
@Transactional(rollbackFor = Exception.class)
java里面將派生于Error或者RuntimeException(比如空指針,1/0)的異常稱為unchecked異常,其他繼承自java.lang.Exception得異常統(tǒng)稱為Checked Exception,如IOException、TimeoutException等
如果先調(diào)用deleteUser(),那么deleteUserA()是不會回滾的,其原因就是@Transactional根本沒生成代理,例如:
public void deleteUser() throws MyException{deleteUser2(); // 事物失效}@Transactionalpublic void deleteUser2() throws MyException{userMapper.deleteUserA();int i = 1 / 0;userMapper.deleteUserB();}
項(xiàng)目中沒有配置事務(wù)管理器,需要在配置類或者配置文件中配置,因?yàn)轫?xiàng)目是多數(shù)據(jù)源的,所以要區(qū)別配置不同數(shù)據(jù)源的事務(wù)管理器,如下:
@Primary@Bean(name = "db1")public DataSource getDataSource() {return createDataSource();}@Bean(name = "db1TransactionManager")public PlatformTransactionManager txManager(@Qualifier("db1") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
@Bean(name = "db2") public DataSource getDataSource() { return buildDataSource(); } @Bean(name = "db2TransactionManager") public PlatformTransactionManager txManager(@Qualifier("db2") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }
可以看到,兩個事務(wù)管理器配置了不同的beanName,接下來只需要 在需要事務(wù)控制的位置加上該事務(wù)管理器的name就可以完美解決!
@Override @Transactional(value = "db1TransactionManager",rollbackFor = Exception.class) public int updateOrInsert(BaseRequest<BankTemplateDto> param) { ... }
如下的方式deleteUserA()也不會回滾,因?yàn)閟pring實(shí)現(xiàn)事務(wù)的原理是通過ThreadLocal把數(shù)據(jù)庫連接綁定到當(dāng)前線程中,新開啟一個線程獲取到的連接就不是同一個了,例如:
@Transactionalpublic void deleteUser() throws MyException{userMapper.deleteUserA();try {//休眠1秒,保證deleteUserA先執(zhí)行Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {int i = 1/0;userMapper.deleteUserB();}).start(); }
注意傳播屬性的設(shè)置,一般情況下,propagation屬性無需配置。會使用默認(rèn)配置,即:PROPAGATION_REQUIRED,有些propagation屬性會導(dǎo)致事務(wù)不會觸發(fā),一定要注意:
PROPAGATION_SUPPORTS: 如果存在事務(wù),則進(jìn)入事務(wù);否則,以非事務(wù)方式運(yùn)行。
PROPAGATION_NOT_SUPPORTED: 如果存在事務(wù),則掛起事務(wù),并以非事務(wù)方式運(yùn)行。
PROPAGATION_NEVER: 以非事務(wù)形式運(yùn)行,如果存在事務(wù),則拋出異常。
propagation屬性
propagation
代表事務(wù)的傳播行為,默認(rèn)值為 Propagation.REQUIRED
,其他的屬性信息如下:
Propagation.REQUIRED
:如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則創(chuàng)建一個新的事務(wù)。( 也就是說如果A方法和B方法都添加了注解,在默認(rèn)傳播模式下,A方法內(nèi)部調(diào)用B方法,會把兩個方法的事務(wù)合并為一個事務(wù) )
Propagation.SUPPORTS
:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。
Propagation.MANDATORY
:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則拋出異常。
Propagation.REQUIRES_NEW
:重新創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。( 當(dāng)類A中的 a 方法用默認(rèn)Propagation.REQUIRED
模式,類B中的 b方法加上采用 Propagation.REQUIRES_NEW
模式,然后在 a 方法中調(diào)用 b方法操作數(shù)據(jù)庫,然而 a方法拋出異常后,b方法并沒有進(jìn)行回滾,因?yàn)?code>Propagation.REQUIRES_NEW會暫停 a方法的事務(wù) )
Propagation.NOT_SUPPORTED
:以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。
Propagation.NEVER
:以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。
Propagation.NESTED
:和 Propagation.REQUIRED 效果一樣。
isolation 屬性
isolation
:事務(wù)的隔離級別,默認(rèn)值為 Isolation.DEFAULT
。
Isolation.DEFAULT:使用底層數(shù)據(jù)庫默認(rèn)的隔離級別。
Isolation.READ_UNCOMMITTED
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE
timeout 屬性
timeout
:事務(wù)的超時時間,默認(rèn)值為 -1。如果超過該時間限制但事務(wù)還沒有完成,則自動回滾事務(wù)。
readOnly 屬性
readOnly
:指定事務(wù)是否為只讀事務(wù),默認(rèn)值為 false;為了忽略那些不需要事務(wù)的方法,比如讀取數(shù)據(jù),可以設(shè)置 read-only 為 true。
rollbackFor 屬性
rollbackFor
:用于指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個異常類型。
noRollbackFor屬性**
noRollbackFor
:拋出指定的異常類型,不回滾事務(wù),也可以指定多個異常類型
上述就是小編為大家分享的Spring中@Transactional事務(wù)不生效如何解決了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。