
列表1事務(wù)不會(huì)回滾
列表2事務(wù)不會(huì)回滾
@Transactional 注解解析
定義
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
...
}
@Transactional 的特性有:
可以在類或方法(接口)上標(biāo)注
標(biāo)注在類上:類中所有方法都進(jìn)行事務(wù)處理
標(biāo)注在接口、實(shí)現(xiàn)類的方法上:方法進(jìn)行事務(wù)處理
優(yōu)先級:方法注解 > 類注解
屬性
value 和 transactionManager:事務(wù)管理器,PlatformTransactionManager 接口
propagation:事務(wù)傳播機(jī)制,默認(rèn)值為 Propagation.REQUIRED
事務(wù)傳播機(jī)制 特性 備注
REQUIRED 如果存在一個(gè)事務(wù),則支持當(dāng)前事務(wù)。如果沒有事務(wù)則開啟一個(gè)新的事務(wù)。 默認(rèn)值,也是絕大多數(shù)場景
SUPPORTS 如果存在一個(gè)事務(wù),支持當(dāng)前事務(wù)。如果沒有事務(wù),則非事務(wù)的執(zhí)行。
MANDATORY 如果已經(jīng)存在一個(gè)事務(wù),支持當(dāng)前事務(wù)。如果沒有一個(gè)活動(dòng)的事務(wù),則拋出異常。
REQUIRES_NEW 總是開啟一個(gè)新的事務(wù)。如果一個(gè)事務(wù)已經(jīng)存在,則將這個(gè)存在的事務(wù)掛起。
NOT_SUPPORTED 總是非事務(wù)地執(zhí)行,并掛起任何存在的事務(wù)。
NEVER 總是非事務(wù)地執(zhí)行,如果存在一個(gè)活動(dòng)事務(wù),則拋出異常。
NESTED 如果一個(gè)活動(dòng)的事務(wù)存在,則運(yùn)行在一個(gè)嵌套的事務(wù)中。如果沒有活動(dòng)事務(wù),則按REQUIRED屬性執(zhí)行。
isolation:事務(wù)隔離級別
事務(wù)隔離級別 特性 備注
DEFAULT 使用后端數(shù)據(jù)庫默認(rèn)的隔離級別 默認(rèn)值,也是絕大多數(shù)場景
READ_UNCOMMITTED 最低的隔離級別,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀
READ_COMMITTED 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生
REPEATABLE_READ 對同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。
SERIALIZABLE 最高的隔離級別,完全服從ACID的隔離級別。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說,該級別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級別。
timeout:事務(wù)超時(shí)時(shí)間,單位是秒。是指一個(gè)事務(wù)所允許執(zhí)行的最長時(shí)間,如果超過該時(shí)間限制但事務(wù)還沒有完成,則自動(dòng)回滾事務(wù)。默認(rèn)值是當(dāng)前數(shù)據(jù)庫默認(rèn)事務(wù)過期時(shí)間。
readOnly:事務(wù)是否是只讀的,默認(rèn)是 false。對于只讀查詢,可以指定事務(wù)類型為 readonly,即只讀事務(wù)。由于只讀事務(wù)不存在數(shù)據(jù)的修改, 因此數(shù)據(jù)庫將會(huì)為只讀事務(wù)提供一些優(yōu)化手段
rollbackFor:設(shè)置需要進(jìn)行回滾的異常類數(shù)組,當(dāng)方法中拋出指定異常數(shù)組中的異常時(shí),則事務(wù)回滾。
rollbackForClassName:設(shè)置需要進(jìn)行回滾的異常類名稱數(shù)組,當(dāng)方法中拋出指定異常名稱數(shù)組中的異常時(shí),則進(jìn)行事務(wù)回滾。
noRollbackFor:設(shè)置不需要進(jìn)行回滾的異常類數(shù)組,當(dāng)方法中拋出指定異常數(shù)組中的異常時(shí),不進(jìn)行事務(wù)回滾。
noRollbackForClassName:設(shè)置不需要進(jìn)行回滾的異常類名稱數(shù)組,當(dāng)方法中拋出指定異常名稱數(shù)組中的異常時(shí),不進(jìn)行事務(wù)回滾。
最后四個(gè)參數(shù)都與回滾有關(guān)。但是,不推薦使用 rollbackForClassName 和 noRollbackForClassName 兩個(gè)參數(shù),而用另外兩個(gè)參數(shù)來代替,從參數(shù)的類型上就可以看出區(qū)別,使用字符串的缺點(diǎn)在于:如果不是用類的完整路徑,就可能導(dǎo)致回滾設(shè)置對位于不同包中的同名類都生效;且如果類名寫錯(cuò),也無法得到 IDE 的動(dòng)態(tài)提示。
使用 @Transactional 需要注意的地方
@Transactional 只能應(yīng)用到 public 方法才有效
在默認(rèn)配置中,Spring FrameWork 的事務(wù)框架代碼只會(huì)將出現(xiàn) runtime, unchecked 異常的事務(wù)標(biāo)記為回滾;也就是說事務(wù)中拋出的異常是 RuntimeException 或其子類,這樣事務(wù)才會(huì)回滾(默認(rèn)情況下 Error 也會(huì)導(dǎo)致事務(wù)回滾)。但是,在默認(rèn)配置的情況下,所有的 checked 異常都不會(huì)引起事務(wù)回滾。
Spring 中 @Transactional 注解的限制 - 沒有事務(wù)的方法去調(diào)用有事務(wù)的方法
同一個(gè)類中,沒有事務(wù)的方法去調(diào)用有事務(wù)的方法 - 事務(wù)會(huì)失效
示例代碼
/**
<h2>在private方法上標(biāo)注transactional, 事務(wù)無效</h2>
-
*/
@Transactional
public void anotherOneSaveMethod() {extraAdDao.save(new ExtraAd("qinyi"));
throw new RuntimeException();
}
/**
<h2>同一個(gè)類中, 一個(gè)不標(biāo)注事務(wù)的方法去調(diào)用 transactional 的方法, 事務(wù)會(huì)失效</h2>
-
*/
@Override
// @Transactional
public void NonTransactionalCanNotRollback() {anotherOneSaveMethod();
}
執(zhí)行過程中拋出的異常
java.lang.RuntimeException
at com.imooc.extra.service.impl.SpringTransactionImpl.anotherOneSaveMethod(SpringTransactionImpl.java:130)
at com.imooc.extra.service.impl.SpringTransactionImpl.NonTransactionalCanNotRollback(SpringTransactionImpl.java:140)
at com.imooc.extra.service.impl.SpringTransactionImplbafcf296.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684)
at com.imooc.extra.service.impl.SpringTransactionImplae164a74.NonTransactionalCanNotRollback(<generated>)
at com.imooc.extra.service.TransactionTest.testNonTransactionalCanNotRollback(TransactionTest.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
...
從拋出的異??梢钥吹?,首先,我們拿到的是代理對象,調(diào)用 NonTransactionalCanNotRollback 方法。但是 NonTransactionalCanNotRollback 方法在調(diào)用 anotherOneSaveMethod 的時(shí)候卻是原始對象的 anotherOneSaveMethod。所以,這里的調(diào)用根本就沒有事務(wù)的存在,導(dǎo)致事務(wù)失效。
不同類中,沒有事務(wù)的方法去調(diào)用有事務(wù)的方法 - 事務(wù)不會(huì)失效
示例代碼
@Service
public class AnotherSpringTransaction {
private final SpringTransactionImpl springTransaction;
@Autowired
public AnotherSpringTransaction(SpringTransactionImpl springTransaction) {
this.springTransaction = springTransaction;
}
/**
* <h2>不同類中, 一個(gè)不標(biāo)注事務(wù)的方法去調(diào)用 transactional 的方法, 事務(wù)不會(huì)失效</h2>
* */
public void TransactionalCanRollback() {
springTransaction.anotherOneSaveMethod();
}
}
執(zhí)行過程中拋出的異常
java.lang.RuntimeException
at com.imooc.extra.service.impl.SpringTransactionImpl.anotherOneSaveMethod(SpringTransactionImpl.java:130)
at com.imooc.extra.service.impl.SpringTransactionImplbafcf296.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
從拋出的異??梢钥吹?,首先,我們拿到的是代理對象,再去調(diào)用anotherOneSaveMethod。所以,這就有事務(wù)了,即事務(wù)不會(huì)失效。