spring中常見事務失效處理分析
- 寫在前面
- 一、事務基礎
- 1.1、定義:
- 1.2、四大特性(A-C-I-D)
- 1.3、常用事務管理
- 二、Spring 事務
- 2.1、Spring中事務基礎
- 2.2、要注意的地方
- 2.2、常見異常
- 2.3、Spring中常用事務處理操作 / 場景,要注意的地方
- 2.3.1、 捕捉異常, 導緻不能復原
- 2.3.2、 自定義異常,導緻事務不復原
- 2.3.3、 嵌套方法
- 2.3.4、 同一個類中, 一個不标注事務的方法去調用 transactional 的方法, 事務會失效
- 三、Mysql 事務
- 四、Oracle 事務
- 五、Nosql 事務
- 六、分布式事務
- 6.1、出現原因
- 6.2、JTA
- 七、總結
寫在前面
隻是總結,分析
一、事務基礎
1.1、定義:
資料庫幾乎是所有系統的核心子產品,它将資料有條理地儲存在儲存媒體(磁盤)中,
并在邏輯上,将資料以結構化的形态呈現給使用者。支援資料的增、删、改、查,并在過程中保障資料的正确且可靠。
1.2、四大特性(A-C-I-D)
- 原子性(Atomicity): 事務要麼全部完成,要麼全部取消。 如果事務崩潰,狀态回到事務之前(事務復原)。
- 隔離性(Isolation): 如果2個事務 T1 和 T2 同時運作,事務 T1 和 T2 最終的結果是相同的,不管 T1和T2誰先結束。
- 持久性(Durability): 一旦事務送出,不管發生什麼(崩潰或者出錯),資料要儲存在資料庫中。
- 一緻性(Consistency): 隻有合法的資料(依照關系限制和函數限制)才能寫入資料庫。
場景分析
如何同時保證上述交易中,A賬戶總金額減少一個億,B賬戶總金額增加一個億? A
A賬戶如果同時在和C賬戶交易(T2),如何讓這兩筆交易互不影響? I
如果交易完成時資料庫突然崩潰,如何保證交易資料成功儲存在資料庫中? D
如何在支援大量交易的同時,保證資料的合法性(沒有錢憑空産生或消失) ? C
1.3、常用事務管理
主要包括,以下幾種
- Mysql 事務
- Oracle 事務
- Nosql 事務
- Spring 事務
- 分布式 事務
二、Spring 事務
2.1、Spring中事務基礎
Spring中事務基礎很簡單,兩種方式配置事務,XML或者注解方式,以下基于注解方式,介紹
實作原理:基于Spring AOP,可自行學習
七種事務機制
1. TransactionDefinition.PROPAGATION_REQUIRED:
如果目前存在事務,則加入該事務;如果目前沒有事務,則建立一個新的事務。這是預設值。
2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:
建立一個新的事務,如果目前存在事務,則把目前事務挂起。
3. TransactionDefinition.PROPAGATION_SUPPORTS:
如果目前存在事務,則加入該事務;如果目前沒有事務,則以非事務的方式繼續運作。
4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
以非事務方式運作,如果目前存在事務,則把目前事務挂起。
5. TransactionDefinition.PROPAGATION_NEVER:
以非事務方式運作,如果目前存在事務,則抛出異常。
6. TransactionDefinition.PROPAGATION_MANDATORY:
如果目前存在事務,則加入該事務;如果目前沒有事務,則抛出異常。
7. TransactionDefinition.PROPAGATION_NESTED:
如果目前存在事務,則建立一個事務作為目前事務的嵌套事務來運作;
如果目前沒有事務,則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。
2.2、要注意的地方
- 預設事務:
Propagation propagation() default Propagation.REQUIRED
當然,可在注解中,根據需要,配置事務機制
- 聲明位置:
類上,所有方法預設事務,不推薦,影響了查詢效率(AOP)
方法上,該方法預設事務,但也要注意細節,包括修飾符,嵌套事務,異常處理等等,都會影響事務是否正确配置
- 修飾符
方法必須 public 修飾
- 異常處理
系統中的異常處理方式,很多,主要 return,throw ,try()catch(){}三種方式,隻有throw的異常,才會觸發事務
- 不同類嵌套事務
不同類之間的方法調用,如類A的方法a()調用類B的方法b(),這種情況事務是正常起作用的。隻要方法a()或b()配置了事務,運作中就會開啟事務,産生代理。
若兩個方法都配置了事務,兩個事務具體以何種方式傳播,取決于設定的事務傳播特性。
- 同類嵌套事務
@Service
public class TestImpl implements TestService {
@Autowired
private Dao dao;
public void insertUser(User user) {
dao.insertUser(user);
this.selectUser(user.getId());
}
@Transactional
public String selectUser(int id) {
throw new RuntimeException();
}
}
這種同類中,事務方法的調用問題,預設情況下,隻有來自外部的方法調用才會被AOP代理捕獲,即是,類内部方法調用本類内部的其他方法并不會引起事務行為,即使被調用方法使用@Transactional注解進行修飾
解決方案:五種
方法1:将事務方法放到另一個類中(或者單獨開啟一層,取名“事務層”)進行調用,即符合了在對象之間調用的條件。
方法2:擷取本對象的代理對象,再進行調用.
- Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy=“true”/>
- 在xxxServiceImpl中,用(xxxService)(AopContext.currentProxy()),擷取到xxxService的代理類,再調用事務方法,強行經過代理類,激活事務切面。
方法3:
很多時候,方法内調用又希望激活事務,是由于同一個方法既有DAO操作又有I/O等耗時操作,不想讓耗時的I/O造成事務的太長耗時(比如新增商品同時需要寫入庫存)。此時,可以将I/O做成異步操作(如加入線程池),而加入線程池的操作即便加入事務也不會導緻事務太長,問題可以迎刃而解。
方法4:手寫事務
自行學習
方法5:
接口内部方法調用的錯誤實作,可以用AspectJ 取代 Spring AOP 代理
2.2、常見異常
- Connection is read-only,…操作隻讀事務異常
- UnexpectedRollbackException: Transaction rolled … 方法嵌套事務異常
- zzz
- xxx
2.3、Spring中常用事務處理操作 / 場景,要注意的地方
2.3.1、 捕捉異常, 導緻不能復原
代碼示例,此處事務不會復原
@Override
@Transactional
public void CatchExceptionCanNotRollback() {
try {
extraAdDao.save(new ExtraAd("otii"));
throw new RuntimeException();
} catch (Exception ex) {
ex.printStackTrace();
}
}
解決方案 :
- 1、手動處理如下
@Override
@Transactional
public void CatchExceptionCanNotRollback() {
try {
extraAdDao.save(new ExtraAd("otii"));
throw new RuntimeException();
} catch (Exception ex) {
ex.printStackTrace();
// 手動标記復原
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
- 2、不捕獲
@Override
@Transactional
public void RuntimeExceptionCanRollback() {
extraAdDao.save(new ExtraAd("otii"));
throw new RuntimeException();
}
2.3.2、 自定義異常,導緻事務不復原
代碼示例,無法復原
@Override
@Transactional
public void NotRuntimeExceptionCanNotRollback() throws CustomException {
try {
extraAdDao.save(new ExtraAd("otii"));
throw new RuntimeException();
} catch (Exception ex) {
throw new CustomException(ex.getMessage());
}
}
解決方案 :
- 1、異常轉換
@Override
@Transactional(rollbackFor = {CustomException.class})
public void AssignExceptionCanRollback() throws CustomException {
try {
extraAdDao.save(new ExtraAd("otii"));
throw new RuntimeException();
} catch (Exception ex) {
throw new CustomException(ex.getMessage());
}
}
注意:這樣 指定也是可以的
@Override
@Transactional(rollbackFor = {Exception.class})
public void AssignExceptionCanRollback() throws CustomException {
try {
extraAdDao.save(new ExtraAd("otii"));
throw new RuntimeException();
} catch (Exception ex) {
throw new CustomException(ex.getMessage());
}
}
2.3.3、 嵌套方法
代碼示例,此處事務 不復原
public void oneSaveMethod() {
extraAdDao.save(new ExtraAd("otii"));
}
@Override
@Transactional
public void RollbackOnlyCanRollback() throws Exception {
oneSaveMethod();
throw new Exception("");
}
解決方案:
- 1、事務復原 - 異常指定
public void oneSaveMethod() {
extraAdDao.save(new ExtraAd("otii"));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void RollbackOnlyCanRollback() throws Exception {
oneSaveMethod();
throw new Exception("");
}
- 2、配置事務傳播機制
public void oneSaveMethod() {
extraAdDao.save(new ExtraAd("otii"));
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public void RollbackOnlyCanRollback() throws CustomException {
oneSaveMethod();
throw new CustomException("");
}
2.3.4、 同一個類中, 一個不标注事務的方法去調用 transactional 的方法, 事務會失效
代碼示例,如下,事務不復原
@Override
public void NonTransactionalCanNotRollback() {
anotherOneSaveMethod();
}
@Transactional
public void anotherOneSaveMethod() {
extraAdDao.save(new ExtraAd("otii"));
throw new RuntimeException();
}
這裡會有奇怪的現象,就是 會莫名 commit
解決方案 :
- 1、調用事務方法時,加上 @Transactional 注解,即可解決
三、Mysql 事務
待記錄
四、Oracle 事務
待記錄
五、Nosql 事務
六、分布式事務
6.1、出現原因
6.2、JTA
JTA(Java Transaction API)提供了跨資料庫連接配接(或其他JTA資源)的事務管理能力。JTA事務管理則由JTA容器實作,J2ee架構中事務管理器與應用程式,資料總管,以及應用伺服器之間的事務通訊。
還有