您好,登錄后才能下訂單哦!
這篇“Spring七大事務傳遞機制的實現原理是什么”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Spring七大事務傳遞機制的實現原理是什么”文章吧。
首先,我們通過org.springframework.transaction.annotation.Propagation來了解一下spring事務的傳播定義:
1. REQUIRED(默認):
Support a current transaction, create a new one if none exists.
支持當前事務,如果沒有則創建一個新的
2. SUPPORTS
Support a current transaction, execute non-transactionally if none exists.
支持當前事務,如果沒有則不使用事務
3. MANDATORY
Support a current transaction, throw an exception if none exists
支持當前事務,如果沒有事務則報錯
4. REQUIRED_NEW
Create a new transaction, and suspend the current transaction if one exists.
新建一個事務,同時將當前事務掛起
5. NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one exists
以無事務的方式執行,如果當前有事務則將其掛起
6. NEVER
Execute non-transactionally, throw an exception if a transaction exists.
以無事務的方式執行,如果當前有事務則報錯
7. NESTED
Execute within a nested transaction if a current transaction exists,behave like PROPAGATION_REQUIRED else
如果當前有事務,則在當前事務內部嵌套一個事務,內部事務的回滾不影響當前事務。如果當前沒有事務,就相當于REQUIRED
Note: Actual creation of a nested transaction will only work on specific transaction managers. Out of the box, this only applies to
the JDBC DataSourceTransactionManager when working on a JDBC 3.0 driver.
Some JTA providers might support nested transactions as well.
注意:該定義只能在JDBC3.0驅動下的DataSourceTransactionManager事務管理器中使用,有些JTA事務可能也會支持
接下來我們通過代碼驗證一下spring事務的傳遞性,在UserServiceImpl類添加兩個方法如下:
@Transactional(propagation = Propagation.NEVER) public User findById(Long id) { User user = userMapper.findById(id); System.out.println("find user:"+user); return user; } @Transactional public void transactionTest(int t) { findById(t+0L); }
我們調用transactionTest方法,transactionTest沒有配置Propagation,所以默認是REQUIRED,會在當前新建一個事務。transactionTest內部調用findById,由于findById事務傳播定義為NEVER,表明它當前不能有事務,按理說這里會拋出異常,但是我們利用junit執行后發現,transactionTest是可以正常執行的。
事實上,如果使用@Transaction方法里嵌套調用的是同一個類的方法,spring代理會忽略嵌套方法的@Transaction配置。但是,如果是其他注入對象的方法,那么@Transaction配置就會生效。我們將上面的transactionTest方法的事務傳播定義為NERVER,并新增一個insert操作,即使insert啟用了事務并且拋出異常,但是事務不會生效,也不會有回滾的說法,程序會拋出異常但是數據會保存到數據庫中:
@Transactional(propagation = Propagation.NEVER) public void transactionTest(int t) { findById(t+0L); insertUser("huangxl","abc123"); } @Transactional public int insertUser(String name, String password) { User user = new User(); user.setPassword(password); user.setUsername(name); int insertCount = userMapper.insertEntity(user); if(insertCount == 1 ){ throw new RuntimeException("test transaction roll back"); } return insertCount; }
接下來我們來測試不同類之間的方法(事務)調用,以下的測試都是基于junit執行TransactionTestServiceImpl.test()方法
下面我們將UserService注入到TransactionTestServiceImpl中,test方法使用@Transactional,UserService findById事務傳播定義不變,還是NERVER。
UserserviceImpl: @Service public class TransactionTestServiceImpl implements TransactionTestService { @Autowired private UserService userService; @Override @Transactional public void test() { userService.findById(1L); } } TransactionTestServiceImpl: @Service public class UserServiceImpl implements UserService { @Override @Transactional(propagation = Propagation.NEVER) public User findById(Long id) { User user = userMapper.findById(id); System.out.println("find user:"+user); return user; } }
由于test默認啟用了事務,findById不允許當前有事務,所以我們執行test方法后會發現程序拋出了異常:
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never’
結論:
NERVER 不允許當前存在事務
UserserviceImpl: @Transactional public int insertUser(String name, String password) { User user = new User(); user.setPassword(password); user.setUsername(name); int insertCount = userMapper.insertEntity(user); if(insertCount == 1 ){ throw new RuntimeException("test transaction roll back"); } return insertCount; } TransactionTestServiceImpl: @Transactional public void test() { try { userService.insertUser("abc", "123"); } catch (Exception e) { //do Nothing } userMapper.updateUserPassWord(1L, "456"); }
我們會發現,即使捕獲了userService.insertUser拋出的異常,test還是把insertUser和updateUserPassword操作當成是一個整體,整個事務還是回滾了,程序拋出了下面的異常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
結論:
REQUIRED子事務會影響當前事務的提交、回滾
UserserviceImpl: @Transactional(propagation = Propagation.NESTED) public int insertUser(String name, String password) { User user = new User(); user.setPassword(password); user.setUsername(name); int insertCount = userMapper.insertEntity(user); if(insertCount == 1 ){ throw new RuntimeException("test transaction roll back"); } return insertCount; } TransactionTestServiceImpl: @Transactional public void test() { try { userService.insertUser("abc", "123"); } catch (Exception e) { //do Nothing } userMapper.updateUserPassWord(1L, "456"); }
程序正常運行,因為NESTED內部事務回滾不影響外部事務。假如這個時候我們把test的@Transactional去掉再運行test方法,發現insertUser沒有插入用戶信息,說明當前沒有事務的情況下,NESTED會默認創建一個事務,類似于REQUIRED。
如果我們把程序改為下面的情況:
UserserviceImpl: @Transactional(propagation = Propagation.NESTED) public int insertUser(String name, String password) { User user = new User(); user.setPassword(password); user.setUsername(name); int insertCount = userMapper.insertEntity(user); return insertCount; } TransactionTestServiceImpl: @Transactional public void test() { userService.insertUser("abc", "123"); int updateRow = userMapper.updateUserPassWord(1L, "456"); if (updateRow == 1) { throw new RuntimeException("transational roll back"); } }
我們會發現沒有插入用戶信息,當前事務和子事務全部回滾。
結論:
NESTED子事務回滾不會影響當前事務的提交(catch回滾異常的情況下),但是當前事務回滾會回滾子事務。也就是說只有當前事務提交成功了,子事務才會提交成功。
UserserviceImpl: @Transactional(propagation = Propagation.REQUIRES_NEW) public int insertUser(String name, String password) { User user = new User(); user.setPassword(password); user.setUsername(name); int insertCount = userMapper.insertEntity(user); return insertCount; } TransactionTestServiceImpl: @Transactional public void test() { userService.insertUser("abc", "123"); int updateRow = userMapper.updateUserPassWord(1L, "456"); if (updateRow == 1) { throw new RuntimeException("transational roll back"); } }
運行結果:程序報錯,但是有用戶信息插入。
將程序改為下面的樣子:
UserserviceImpl: @Transactional(propagation = Propagation.REQUIRES_NEW) public int updateUserPassWorld(Long id, String password) { int update = userMapper.updateUserPassWord(id,password); return update; } TransactionTestServiceImpl: @Transactional public void test() { //當前事務 userMapper.updateUserPassWord(28L, "123456"); //執行REQUIRES_NEW事務 userService.updateUserPassWorld(28L, "000000"); System.out.println("commit"); }
執行程序,發現程序遲遲沒有打印字符串commit,發生了死鎖。
結論:
REQUIRES_NEW會啟用一個新的事務,事務擁有完全獨立的能力,它不依賴于當前事務,執行時會掛起當前事務,直到REQUIRES_NEW事務完成提交后才會提交當前事務,如果當前事務與REQUIRES_NEW 存在鎖競爭,會導致死鎖。
UserserviceImpl: @Transactional(propagation = Propagation.NOT_SUPPORTED) public int updateUserPassWorld(Long id, String password) { int updateRow = userMapper.updateUserPassWord(id,password); if(updateRow ==1 ){ throw new RuntimeException("roll back test"); } return updateRow; } TransactionTestServiceImpl: @Transactional public void test() { userService.updateUserPassWorld(28L, "000000"); }
程序運行報錯,但是id為28的用戶密碼還是更新了。
將程序改為下面這個情況:
UserserviceImpl: @Transactional(propagation = Propagation.NOT_SUPPORTED) public int updateUserPassWorld(Long id, String password) { int update = userMapper.updateUserPassWord(id,password); return update; } TransactionTestServiceImpl: @Transactional public void test() { //當前事務 userMapper.updateUserPassWord(28L, "123456"); //執行REQUIRES_NEW事務 userService.updateUserPassWorld(28L, "000000"); System.out.println("commit"); }
執行程序,發現程序遲遲沒有打印字符串commit,發生了死鎖。
結論:
NOT_SUPPORTED會掛起當前事務,并且NOT_SUPPORTED定義的方法內部不啟用顯示事務,如果NOT_SUPPORTED和當前事務存在鎖競爭,會發生死鎖。
UserserviceImpl: @Transactional(propagation = Propagation.MANDATORY) public int updateUserPassWorld(Long id, String password) { int updateRow = userMapper.updateUserPassWord(id,password); return updateRow; } TransactionTestServiceImpl: public void test() { userService.updateUserPassWorld(28L, "123456"); }
程序運行錯誤:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’
結論:
MANDATORY必須包含在事務中,如果事務不存在,則拋出異常
以上就是關于“Spring七大事務傳遞機制的實現原理是什么”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。