Transaction事務
上期我們講到了jpa的常用操作,查詢、更新、刪除等,但是如果在運算元據庫事務時發生異常,資料會回滾嗎?下面我們來看個例子
UserController新增如下程式碼:
@GetMapping("save1")
public String save1(){
User user = new User();
user.setDptId(1L);
user.setName("a");
user.setAge(18L);
user.setEmail("a@a.com");
user.setHeadImg("headImg1");
this.userJpa.save(user);
//模擬發生了異常
System.out.println(1/0);
return "ok";
}
使用postman請求
localhost:8080/user/save1
執行之後可以看到java後臺報錯了,postman前臺也報出來錯誤,但是資料卻儲存進去了,資料新增了一條記錄
說明即使發生了異常,資料還是會儲存進去資料庫,那應該怎麼辦呢?試試在save1方法上加一個@Transactional的註解。
我們再執行一次。發現錯誤也報出來了,但是資料庫並沒有將新資料插入進去,最新的還是上一次的id為7的記錄,那麼 Transactional註解是幹嘛的呢?
@Transactional是宣告式事務管理程式設計中使用的註解
如下是該註解的屬性,我們需要關注重點關注的是rollback-for和propagation兩個屬性。
屬性名 | 說明 |
---|---|
name | 當在配置檔案中有多個 TransactionManager , 可以用該屬性指定選擇哪個事務管理器。 |
propagation | 事務的傳播行為,預設值為 REQUIRED。 |
isolation | 事務的隔離度,預設值採用 DEFAULT。 |
timeout | 事務的超時時間,預設值為-1。如果超過該時間限制但事務還沒有完成,則自動回滾事務。 |
read-only | 指定事務是否為只讀事務,預設值為 false;為了忽略那些不需要事務的方法,比如讀取資料,可以設定 read-only 為 true。 |
rollback-for | 用於指定能夠觸發事務回滾的異常型別,如果有多個異常型別需要指定,各型別之間可以通過逗號分隔。 |
no-rollback- for | 丟擲 no-rollback-for 指定的異常型別,不回滾事務。 |
rollback-for:只有執行的異常才回滾。但是我們剛剛的程式並沒有指定異常,那是預設的是遇到什麼樣的異常會回滾呢?
- 將UserController中的程式碼稍作修改,手動
throw new Exception("test")
,再執行下postman,發現事務提交了,並沒有回滾。 - 接著我們將註解修改為
@Transactional(rollbackFor = Exception.class)
,再執行postman,事務卻回滾了,並沒有提交,什麼原因? - spring的
@Transactional
註解可以很方便的開啟事務,但是預設只在遇到執行時異常
和Error
時才會回滾,非執行時異常不回滾,即Exception
的子類中,除了RuntimeException
及其子類,其他的類預設不回滾。 - 而rollbackFor屬性可以解決這個問題,
rollbackFor = Exception.class
表示Exception
及其子類的異常都會觸發回滾,同時不影響Error
的回滾。
propagation:這個用得最廣的需求就是業務出錯了,但是日誌必須提交到資料庫。怎麼處理?來看下面的程式碼。
新增LogService類
@Service
public class LogService {
@Resource
private UserJpa userJpa;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(){
User user = new User();
user.setDptId(1L);
user.setName("log");
user.setAge(18L);
user.setEmail("log@log.com");
user.setHeadImg("log");
this.userJpa.save(user);
System.out.println("log");
}
}
新增UserService類:
@Service
public class UserService {
@Resource
private UserJpa userJpa;
@Resource
private LogService logService;
@Transactional(rollbackFor = Exception.class)
public void saveBiz() throws Exception {
System.out.println("save2");
User user = new User();
user.setDptId(1L);
user.setName("biz");
user.setAge(18L);
user.setEmail("biz@biz.com");
user.setHeadImg("biz");
this.userJpa.save(user);
//模擬儲存日誌
this.logService.saveLog();
//模擬發生了異常
throw new Exception("test1");
}
}
UserController新增程式碼
@GetMapping("save2")
public String save2() throws Exception {
//模擬業務操作
this.userService.saveBiz();
return "ok";
}
postman執行下,是不是隻有log的那條記錄插入進去了?biz的沒有插入進去。
注意:同一個業務類裡面 , 即使宣告為
Propagation.REQUIRES_NEW
也不會新啟一個事務。必須呼叫另一個類的Propagation.REQUIRES_NEW
方法才行。所以樣例中是使用UserService
裡面呼叫另一個類LogService
中的saveLog
的方法。
更多原創閱讀:點選