SpringBoot中的事務處理問題
介紹
在實際的業(yè)務場景中,不光數據庫要實現(xiàn)事務,我們的Service層業(yè)務也要實現(xiàn)事務。
例如:要實現(xiàn)數據的刪除,刪除數據庫中的數據并刪除Redis緩存中的數據,他們在一個ServiceImpl的方法中,我們要實現(xiàn)這兩個操作放在一個事務中,兩個操作同時成功 / 失敗,所有,要使用Spring事務。
SpringBoot中事務實現(xiàn)
ServiceImpl的方法 / ServiceImpl類 上加上@Transactional。
- 方法:注解只對public方法有效(因為@Transactional 工作原理是基于AOP實現(xiàn)的),如果在 protected、private 或者默認方法上使用,沒有作用,也不報錯。
- 類:該類的所有 public 方法將都具有該類型的事務屬性
注意:在方法級別使用該注解可以覆蓋類級別使用該注解。
// 這個類中的public方法如果拋出Exception,則該方法會回滾
@Transactional(rollbackFor = Exception.class)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
//方法上注解屬性會覆蓋類注解上的相同屬性
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}@EnableTransactionManagement
SpringBoot啟動類上加上不用加@EnableTransactionManagement注解,SpringBoot自動裝配已經幫我們處理了,SpringBoot項目默認支持事務。
@Transactional
作用位置:接口、接口方法、類以及類方法
Spring 建議不要在接口或者接口方法上使用該注解,因為這只有在使用基于接口的代理時它才會生效。
注意:
默認情況下,只有來自外部的方法調用才會被AOP代理捕獲,也就是,類內部方法調用本類內部的其他方法并不會引起事務行為,即使被調用方法使用@Transactional注解進行修飾。
Transactional類
package org.springframework.transaction.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}注解的屬性
| 屬性 | 類型 | 描述 |
| value | String | 可選的限定描述符,指定使用的事務管理器 |
| propagation | enum: Propagation | 可選的事務傳播行為設置 |
| isolation | enum: Isolation | 可選的事務隔離級別設置 |
| readOnly | boolean | 讀寫或只讀事務,默認讀寫 |
| timeout | int (in seconds granularity) | 事務超時時間設置 |
| rollbackFor | Class對象數組,必須繼承自Throwable | 導致事務回滾的異常類數組 |
| rollbackForClassName | 類名數組,必須繼承自Throwable | 導致事務回滾的異常類名字數組 |
| noRollbackFor | Class對象數組,必須繼承自Throwable | 不會導致事務回滾的異常類數組 |
| noRollbackForClassName | 類名數組,必須繼承自Throwable | 不會導致事務回滾的異常類名字數組 |
在Spring中定義了五種隔離級別常量
一般用默認就可以。
package org.springframework.transaction.annotation;
import org.springframework.transaction.TransactionDefinition;
public enum Isolation {
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
private final int value;
Isolation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}| 常量 | 說明 |
|---|---|
| TransactionDefinition.ISOLATION_DEFAULT | 數據庫默認的隔離級別,MySQL默認采用的 REPEATABLE_READ隔離級別 |
| TransactionDefinition.ISOLATION_READ_UNCOMMITTED | 最低的隔離級別,允許讀取未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀。 |
| TransactionDefinition.ISOLATION_READ_COMMITTED | 允許讀取并發(fā)事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發(fā)生。 |
| TransactionDefinition.ISOLATION_REPEATABLE_READ | 對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發(fā)生。MySQL中通過MVCC解決了該隔離級別下出現(xiàn)幻讀的可能。 |
| TransactionDefinition.ISOLATION_SERIALIZABLE | 串行化隔離級別,該級別可以防止臟讀、不可重復讀以及幻讀,但是串行化會影響性能。 |
Spring定義了七種事務傳播行為
| 類型 | 說明 |
|---|---|
| PROPAGATION_REQUIRED | 如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇 |
| PROPAGATION_SUPPORTS | 支持當前事務,如果當前沒有事務,就以非事務方式執(zhí)行。 |
| PROPAGATION_MANDATORY | 使用當前的事務,如果當前沒有事務,就拋出異常。 |
| PROPAGATION_REQUIRES_NEW | 新建事務,如果當前存在事務,把當前事務掛起。 |
| PROPAGATION_NOT_SUPPORTED | 以非事務方式執(zhí)行操作,如果當前存在事務,就把當前事務掛起。 |
| PROPAGATION_NEVER | 以非事務方式執(zhí)行,如果當前存在事務,則拋出異常。 |
| PROPAGATION_NESTED | 如果當前存在事務,則在嵌套事務內執(zhí)行。如果當前沒有事務,則執(zhí)行與PROPAGATION_REQUIRED類似的操作。 |
Spring事務什么情況下會失效
【1】@Transactional 應用在非 public 修飾的方法上
@Transactional 工作原理是基于AOP實現(xiàn)的
所以,注解必須作用在public的方法上,否則失效。
【2】數據庫引擎是否支持事務(MySql的MyIsam引擎不支持事物)
【3】@Transactional注解屬性 propagation 設置錯誤
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續(xù)運行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異。
【4】@Transactional 注解屬性 rollbackFor 設置錯誤?
- Spring默認是回滾RuntimeException才回滾。
- 當然自定義的RuntimeException異常類也是可以的。
- 如果希望Spring能夠回滾別類型的異常,那就需要使用rollbackFor去指定(當然如果是指定異常的子類,也同樣會回滾)。
【5】同一個類中方法調用,導致@Transactional失效?
在同一個類中,沒有加事務的方法A調用加事務的方法B,方法B的事務失效。
( 其實這還是由于使用Spring AOP代理造成的),所以:建議把整個類加上事務注解。
【6】異常被捕獲了?
這個就比較簡單了,就是你自己捕捉到了異常,并且自己處理,并不會拋出到上層的方法調用。
那就不會生效了。
@Transactional
public void contextLoads() {
try {
int i = 0;
}catch (Exception e){
System.out.println("不可以回滾");
}
}可以手動設置回滾
@Transactional
public void contextLoads() {
try {
int i = 0;
}catch (Exception e){
// 手動回滾
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
System.out.println("可以回滾");
}
}【7】新開啟一個線程
Spring實現(xiàn)事務的原理是通過ThreadLocal把數據庫連接綁定到當前線程中,新開啟一個線程獲取到的連接就不是同一個了。
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Caused by: java.lang.ClassNotFoundException: org.apache.comm
這篇文章主要介紹了Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.Type異常,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07

