本文出處 Spring 事務傳播行爲
轉載請說明出處
在Spring @Transactional 聲明式事務有一個項屬性propagation
事務傳播行爲,是一個Propagation 枚舉類,有7種類型,對應不一樣使用場景。下面說下這些枚舉代碼含義,在結合代碼加深理解,鞏固學習。html
Spring 屬性 | 說明 |
---|---|
REQUIRED | 支持當前事務,若是不存在事務則建立一個新事務。這個propagation默認屬性 |
SUPPORTS | 支持當前事務,若是不存在事務則按照無事務執行 |
MANDATORY | 支持當前事務,若是不存事務在則拋出異常 |
REQUIRES_NEW | 建立一個新的事務,若是存在當前事務,則將它掛起 |
NOT_SUPPORTED | 無事務執行,若是存在事務將它掛起 |
NEVER | 無事務執行,若是存在當前事務則拋出異常 |
NESTED | 若是當前事務存在,則在嵌套事務中執行,若是不存在事務就像REQUIRED效果 |
先作一個小實驗,寫一個持久化的save方法,設置成禁用事務,用事務方法去調用它,看會發生什麼效果。git
@Transactional(propagation = Propagation.NEVER) public void save(Users users){ usersRepository.save(users); } @Transactional public void multSave(){ Users users = getUsers(); save(users); }
按照上面字段含義解析,save方法不支持事務,在當前事務運行會拋出異常。但是執行結果不是預料那樣,沒有拋出異常,數據寫入到數據庫了。而且咱們能夠在改一次save方法,直接拋出一個RuntimeTime異常,你會發現結果更加明顯。github
@Transactional(propagation = Propagation.NEVER) public void save(Users users){ usersRepository.save(users); throw new RuntimeException("4322"); } @Transactional public void multSave(){ Users users = getUsers(); save(users); }
你會發現save 方法 數據回滾了。
在作多幾回這種實驗你就會發現,在同一個類中,最外層事務註解屬性會覆蓋方法內全部事務註解,好比你的外層設置默認事務傳播行爲,不管調用那種行爲都會按照默認屬性,外層沒有設置事務,調用方法有事務也不會生效的。這個不難理解,實現Spring 事務一個基於AOP的代理類,它根據目標方法註解來加強方法,這個是運行時動態執行的,方法內其餘方法沒有代理類的功能加強,就至關於普通方法效果了。千萬不要在同一個類裏面測試事務傳播行爲,不然你永遠都都得不到結論的。spring
@Transactional public void save(){ Employee employee = new Employee(); employee.setDepartmentId(2); employee.setName("Jorry"); employee.setSalary(60000); repository.save(employee); throw new RuntimeException("33333"); } @Transactional public void multSave(){ Users users = getUsers(); save(users); employeeService.save(); }
執行結果: users、employee寫入失敗,employee的方法拋出異常觸發回滾,也會致使外層方法回滾。在同一個事務中,只要觸發回滾,事務內全部數據操做都會回滾的。數據庫
@Transactional(propagation = Propagation.SUPPORTS) public void save(){ Elimployee employee = new Employee(); employee.setDepartmentId(2); employee.setName("Jorry"); employee.setSalary(60000); repository.save(employee); } @Transactional public void multSave(){ Users users = getUsers(); save(users); employeeService.save(); throw new RuntimeException("3333"); }
執行效果: Usres和Elimployee 都進行回滾了。SUPPORTS會加入調用者事務中,和調用者事務是同一個事務,若是在事務中出現異常所有回滾。即便在employeeService.save()
拋出異常,效果同樣的。ide
@Transactional(propagation = Propagation.REQUIRES_NEW) public void save(){ Employee employee = new Employee(); employee.setDepartmentId(2); employee.setName("Jorry"); employee.setSalary(60000); repository.save(employee); throw new RuntimeException("3333"); } @Transactional public void multSave(){ Users users = getUsers(); save(users); try { employeeService.save(); }catch (RuntimeException e){ }
執行結果: empoyee回滾,users寫入數據庫成功。要處理employeeService.save()異常,否則異常會致使multSave出現異常事務回滾了。加入一個對照,在把employeeService.save()改爲默認屬性,發現users,employee 都會回滾了,還會拋出一個異常學習
org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
Spring官方文檔說到若是內部事務(外部調用者不知道)將事務無提示地標記爲僅回滾,則外部調用者仍會調用commit。外部調用者須要接收一個,UnexpectedRollbackException以清楚地指示已執行回滾。測試
@Transactional(propagation = Propagation.NEVER) public void save(){ Employee employee = new Employee(); employee.setDepartmentId(2); employee.setName("Jorry"); employee.setSalary(60000); repository.save(employee); } @Transactional public void multSave(){ Users users = getUsers(); save(users); employeeService.save(); }
執行結果: 兩個表都寫入失敗,employeeService.save() 拋出異常ui
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
@Transactional(propagation = Propagation.NOT_SUPPORTED) public void save(){ Employee employee = new Employee(); employee.setDepartmentId(2); employee.setName("Jorry"); employee.setSalary(60000); repository.save(employee); throw new RuntimeException("33333"); } @Transactional public void multSave(){ Users users = getUsers(); save(users); employeeService.save(); }
執行結果:employee成功寫入數據庫,employee 將事務掛了,用無事務方法執行,users受到異常拋出,觸發事務回滾。若是你把employeeService.save()處理一下,兩種表操做都寫入成功了。spa
@Transactional(propagation = Propagation.MANDATORY) public void save(){ Employee employee = new Employee(); employee.setDepartmentId(2); employee.setName("Jorry"); employee.setSalary(60000); repository.save(employee); } public void multSave(){ Users users = getUsers(); save(users); employeeService.save(); }
執行結果: users 成功寫入數據庫,employee寫入失敗而且拋出異常
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
@Transactional public void multSave(){ Users users = getUsers(); save(users); employeeService.save(); } @Transactional(propagation = Propagation.NESTED) public void save(){ Employee employee = new Employee(); employee.setDepartmentId(2); employee.setName("Jorry"); employee.setSalary(60000); repository.save(employee); }
執行結果: employeeService.save()拋出一個異常,好像JPA的實現不支持嵌套執行,這個看不到效果了。
org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities