SpringBoot注解@Transactional詳解以及事務失效
一、事務的特點ACID
- 原子性(Atomicity):事務最小的執行單位,不允許分割,事務的原子性確保動作要么全部完成,要么完全失敗。
- 一致性(Consistency):執行事務前后,數據保持一致,例如在上面的轉賬例子中,無論事務是否成功,轉賬者和收款人的總額應該是不變的。
- 隔離性(Isolation):并發訪問數據庫時,一個用戶的事務不被其它事務干擾,各并發事務之間的數據庫是獨立的。
- 持久性(Durability):一個事務被提交后,它對數據庫中數據的改變是持久的,即使數據庫發生故障也不應該對其有任何影響。
二、Spring對事務的支持
程序是否支持事務的先決條件是數據庫,比如使用MySQL的話,如果選擇的是innodb,那么支持事務,如果選擇的是myisam,那么從根上就不支持事務了
MySQL怎么保證原子性?
如果要保證原子性,就需要在發生異常時,對已經執行的操作進行回滾,在MySQL中,恢復機制是通過回滾日志實現的,所有事務進行的修改,都會先記錄到這個回滾日志中,然后再執行相關的操作。
如果在執行過程中遇到異常,我們直接利用回滾日志中的信息將數據回滾到修改之前的樣子即可,并且回滾日志會先將數據持久化到磁盤上,這樣就可以保證即便在遇到數據庫突然宕機,當用戶再次重啟數據庫時,數據庫還是能夠通過查回滾日志來回滾之前未完成的事務。
三、Spring支持兩種事務管理
3.1編程事務管理
通過TransactionTemplate或者TransactionManager手動管理事務,在實際應用中卻很少使用,下面通過代碼來演示,使用TransactionTemplate進行編程式事務管理
@Autowired
private TransactionTemplate transactionTemplate;
public void testTransactionTemplate() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(final TransactionStatus transactionStatus) {
try {
//TODO 業務代碼
} catch (final Exception e) {
// 異常時回滾
transactionStatus.setRollbackOnly();
}
}
});
}
使用TransactionManager進行編程式事務管理
@Resource
private PlatformTransactionManager transactionManager;
public void testTransactionManager() {
final TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
//TODO 業務代碼
transactionManager.commit(status);
} catch (final Exception e) {
// 異常時回滾
transactionManager.rollback(status);
}
}
3.2聲明式事務管理
聲明式事務管理,實際上是通過AOP實現,基于@Transactional的注解使用最多
@Transactional
public void testTransactional() {
userInfoDao.save(userInfo);
userInfoDetailDao.save(userInfoDetail);
}
在實際的業務開發中,大家一般使用@Transactional注解來開啟事務,但很多人并不是很清楚這個注解中的參數是什么意思?有什么用?
3.2.1@Transactional的作用范圍
- 方法:推薦將注解用于方法上,不過需要注意的是:該注解只能應用到 public 方法上,否則不生效。
- 類:如果將注解用在類上,表明該類中所有的 public 方法都生效。
- 接口:不推薦在接口上使用。
@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;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
String value() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
@Transactional 的常用參數介紹
屬性名 | 說明 |
propagation | 事務的傳播行為,默認值為 REQUIRED |
isolation | 事務的隔離級別,默認值采用 DEFAULT |
timeout | 事務的超時時間,默認值為-1(不會超時),如果超過該時間限制但事務還沒有完成,則自動回滾事務 |
readOnly | 指定事務是否為只讀事務,默認值為 false |
rollbackFor | 用于指定能夠觸發事務回滾的異常類型,并且可以指定多個異常類型 |
關于傳播行為propagation屬性:
- REQUIRED:支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
- SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行。
- MANDATORY:支持當前事務,如果當前沒有事務,就拋出異常。
- REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。
- NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
- NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。
- NESTED:支持當前事務,如果當前事務存在,則執行一個嵌套事務,如果當前沒有事務,就新建一個事務。
3.2.2@Transactional失效場景
- 非 public 修飾的方法;
- timeout 超時時間設置過小;
- 代碼中使用 try/catch 處理異常;
- 調用類內部的@Transactional 方法;
- 數據庫不支持事務。