參考:https://segmentfault.com/a/1190000013341344
例子:
准備 對日志log表的service層操作

package com.wing.my.cloud.system.modular.system.service; import com.wing.my.cloud.system.modular.system.definedLog.entity.DefineLogEntity; import com.wing.my.cloud.system.modular.system.mapper.DefineLogMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * <p> * 測試事務A * </p> * */ @Service public class Transaction1Service { @Resource DefineLogMapper defineLogMapper; @Transactional(propagation = Propagation.REQUIRED) public void addLog_Required(){ DefineLogEntity defineLogEntity = new DefineLogEntity(); defineLogEntity.setClassName("Transaction1Service"); defineLogEntity.setMethodName("addLogRequired"); defineLogMapper.insert(defineLogEntity); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void addLog_REQUIRES_NEW(){ DefineLogEntity defineLogEntity = new DefineLogEntity(); defineLogEntity.setClassName("Transaction1Service"); defineLogEntity.setMethodName("addLogREQUIRES_NEW"); defineLogMapper.insert(defineLogEntity); } @Transactional(propagation = Propagation.NESTED) public void addLog_NESTED(){ DefineLogEntity defineLogEntity = new DefineLogEntity(); defineLogEntity.setClassName("Transaction1Service"); defineLogEntity.setMethodName("addLogNESTED"); defineLogMapper.insert(defineLogEntity); } }
對user表的service層的操作

package com.wing.my.cloud.system.modular.system.service; import com.wing.my.cloud.system.modular.system.entity.User; import com.wing.my.cloud.system.modular.system.mapper.UserMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * <p> * 測試事務B * </p> * */ @Service @Slf4j public class Transaction2Service { @Resource UserMapper userMapper; @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED(){ User user = User.builder() .account("張三REQUIRED") .status("ENABLE") .build(); userMapper.insert(user); } @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException(){ User user = User.builder() .account("張三REQUIRED運行時異常").status("ENABLE").build(); userMapper.insert(user); throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾"); } /** * 因為異常被吃掉,所以走不成事務,不會回滾 */ @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException_Try(){ User user = User.builder() .account("張三REQUIRED運行時異常被cacth掉").status("ENABLE").build(); userMapper.insert(user); try { throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾"); } catch (Exception e) { log.error("異常被吃掉"); } } @Transactional(noRollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException_NoRollbackFor(){ User user = User.builder() .account("張三REQUIRED運行時異常").status("ENABLE").build(); userMapper.insert(user); throw new RuntimeException("加上noRollbackFor也不會走事務。"); } @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_Exception() throws Exception{ User user = User.builder() .account("張三REQUIRED非運行時異常").status("ENABLE").build(); userMapper.insert(user); throw new Exception("Exception是非運行時異常,事務失效"); } @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED) public void addUser_REQUIRED_Exception_RollbackFor() throws Exception{ User user = User.builder() .account("張三REQUIRED非運行時異常").status("ENABLE").build(); userMapper.insert(user); throw new Exception("Exception是非運行時異常,事務失效"); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void addUser_REQUIRES_NEW(){ User user = User.builder() .account("李四REQUIRES_NEW") .status("ENABLE") .build(); userMapper.insert(user); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void addUser_REQUIRES_NEW_Exception(){ User user = User.builder() .account("李四REQUIRES_NEW運行時異常").build(); userMapper.insert(user); throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾"); } @Transactional(propagation = Propagation.NESTED) public void addUser_NESTED(){ User user = User.builder() .account("李四NESTED") .status("ENABLE") .build(); userMapper.insert(user); } @Transactional(propagation = Propagation.NESTED) public void addUser_NESTED_Exception(){ User user = User.builder() .account("李四NESTED運行時異常").status("ENABLE").build(); userMapper.insert(user); throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾"); } }
對事務的操作

package com.wing.my.cloud.system.modular.system.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * <p> * 事務嵌套 * </p> * */ @Service @Slf4j public class Transaction3Service { @Resource Transaction1Service transaction1Service; @Resource Transaction2Service transaction2Service; /** * log添加一條數據 * user添加一條數據 * @throws Exception */ public void exception失效() throws Exception{ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_Exception(); log.info("只有在返回的是不受檢查異常才會有效,exception是檢查異常。不會走事務"); } /** * log添加一條數據 * @throws Exception */ public void exception失效但是事務_加上rollbackFor可以走事務() throws Exception{ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_Exception_RollbackFor(); log.info("Exception加上rollbackFor會走事務"); } /** * log添加一條數據 */ public void RuntimeException可以走事務(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_RuntimeException(); log.info("只有在返回的是不受檢查異常才會有效,RuntimeException是非檢查異常。會走事務"); } /** * log添加一條數據 * user添加一條數據 */ public void RuntimeException可以走事務_加上noRollbackFor不會走事務(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_RuntimeException_NoRollbackFor(); log.info("noRollbackFor不會走事務"); } public void 異常被吃掉(){ transaction2Service.addUser_REQUIRED_RuntimeException_Try(); } /** * REQUIRED * 如果當前沒有事務,就新建一個事務。已經存在事務,就加入到這個事務中 * 外層沒有事務,內層Log,內層User有事務。 * 內層事務沒有異常。外層有異常。 * 外層回滾,內層不會回滾。內層的事務是單獨運行的。 * * log添加一條數據 * user添加一條數據 */ public void REQUIRED_外層無事務_兩個內層事務獨立_都無異常(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED(); log.info("兩個事務" + "外層沒有事務,內層有事務.內層都沒有異常。外層有異常。外層回滾,內層不會回滾。因為不在一個事務中"); throw new RuntimeException(); } /** * log添加一條數據 */ public void REQUIRED_外層無事務_兩個內層事務獨立_USER有異常(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_RuntimeException(); log.info("兩個事務" + "外層沒有事務,內層有事務.內層事務User有異常。外層有異常。外層回滾,內層Log沒有異常,不會回滾。內層User有異常,回滾.因為不在一個事務中"); throw new RuntimeException(); } //-----外層不開事務。REQUIRED修飾的內層事務會新開自己的事務。互相獨立,互不干擾。 /** * 都不添加數據 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRED_外層有事務_兩個內層事務加入到外層事務() { transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED(); log.info("一個事務" + "外層有事務,內層兩個都有事務,都沒有異常.事務Log和事務User都加入到外層的事務中。外層回滾,內層也回滾"); throw new RuntimeException(); } /** * 都不添加數據 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRED_外層有事務_兩個內層事務加入到外層事務_事務USER有異常_外層感知到(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRED_RuntimeException(); log.info("一個事務" + "外層有事務,內層加入外層事務。內層拋出異常回滾,外層感知到異常使整個事務都回滾"); throw new RuntimeException(); } /** * 都不添加數據 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRED_外層有事務_兩個內層事務加入到外層事務_外層有異常_事務USER有異常_被catch吃掉_外層感知不到(){ transaction1Service.addLog_Required(); try { transaction2Service.addUser_REQUIRED_RuntimeException(); } catch (Exception e) { log.error("一共一個事務" + "內層事務的異常被吃掉,外層感知不到異常。但是本身外層有異常,外層回滾。內層也回滾"); } throw new RuntimeException(); } //-----外層開啟事務。REQUIRED修飾的內層事務會加入到外層事務中。所有的事務都在同一個事務中了。只要有一個方法回滾。所有的都回滾 /** * 都不添加數據 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRED_外層有事務_兩個內層事務加入到外層事務_事務USER有異常_被catch吃掉_外層感知不到(){ transaction1Service.addLog_Required(); try { transaction2Service.addUser_REQUIRED_RuntimeException(); } catch (Exception e) { log.error("一共一個事務" + "內層事務的異常被吃掉,外層感知不到異常。但是當前事務已經被標記為rollbackOnly了,所以無法提交"); } } //-----外層開啟事務后,內層事務加入到外層事務。內層方法拋出異常回滾,即使被trycatch吃掉。不被外層方法感知到。整個事務依舊回滾。 // ------因為在同一個事務中,只要有異常,都會被察覺到。然后執行回滾。 /** * REQUIRES_NEW * 新建事務,如果當前存在事務,就把當前事務掛起 * * 外層沒有事務,兩個內層都是在自己的事務中 。外層拋出異常回滾不會影響內層的方法。 * log插入一條數據 * user插入一條數據 */ public void REQUIRES_NEW_兩個內層事務獨立(){ transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW(); log.info("一共兩個事務" + "外層沒有事務,內層有事務。兩個內層都沒有異常。外層有異常。外層回滾,內層不會有回滾"); throw new RuntimeException(); } /** * log插入一條數據 */ public void REQUIRES_NEW_兩個內層事務獨立_USER有異常_不會影響LOG事務() { transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW_Exception(); log.info("一共兩個事務。" + "外層沒有事務,內層兩個都有事務,logRequires_new事務沒有異常,插入成功" + "userRequires_new有異常,回滾"); throw new RuntimeException(); } //外層未開啟事務的情況下,REQUIRES_NEW修飾的內層事務 新開自己的事務。互相獨立。互不干擾。 /** * log_requires_new插入一條 * user_requires_new插入一條 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRES_NEW_內層事務獨立存在不加入到外層事務中(){ transaction1Service.addLog_Required(); transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW(); log.info("一共3個事務。" + "logRequired會加入到外層的事務中。logRequires_new和userRequires_new都是單獨的事務。外層事務有異常,回滾。不會插入數據。" + "兩個requires_new都是單獨的事務,沒有異常,插入成功。"); throw new RuntimeException(); } /** * log_requires_new插入一條數據 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRES_NEW_內層事務獨立不會加入到外層事務中_USER事務有異常不會影響到外層和log事務(){ transaction1Service.addLog_Required(); transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW_Exception(); log.info("一共三個事務" + "logRequired會加入到外層的事務中。userRequiresNew返回異常,外層捕獲到異常。logRequired回滾" + "logrequires_new是單獨的事務,沒有異常,插入成功。" + "userRequires_new是單獨的事務,有異常,回滾。"); } /** * logRequired插入成功 * logRequires_new插入成功 */ @Transactional(propagation = Propagation.REQUIRED) public void REQUIRES_NEW_內層事務獨立_內層USER事務被吃掉(){ transaction1Service.addLog_Required(); transaction1Service.addLog_REQUIRES_NEW(); try { transaction2Service.addUser_REQUIRES_NEW_Exception(); } catch (Exception e) { log.error("一共有三個事務。" + "內層logRequired加入到外層事務中。因為userRequires_new返回的異常被吃掉。外層感知不到異常。沒有回滾。插入成功" + "內層userRequires_new是單獨的事務。沒有異常,插入成功" + "內層userRequires_newException是單獨的事務,有異常,進行回滾。不會插入成功"); } } /** * logRequires_new插入成功 * userRequires_new插入成功 */ @Transactional(propagation = Propagation.REQUIRES_NEW) public void REQUIRES_NEW新開事務_REQUIRED加入外層事務(){ transaction1Service.addLog_Required(); transaction1Service.addLog_REQUIRES_NEW(); transaction2Service.addUser_REQUIRES_NEW(); log.info("一共3個事務。" + "外層一個。logRequired加入到外層事務。logRequires_new新創建一個事務.userRequires_new新創建一個事務" + "3個內層事務沒有異常。但是logRequired加入到外層事務中了,外層事務有異常,不會插入成功。另外兩個內層事務插入成功。" ); throw new RuntimeException(); } //-----外層開啟事務。內層用REQUIRES_NEW修飾的方法依舊會開啟獨立事務。且與外層事務也獨立,互相獨立,互不干擾。 /** * 不添加 */ @Transactional(propagation = Propagation.REQUIRED) public void NESTED_內層事務是外層的子事務_外層有異常外層回滾_兩個子事務都回滾(){ transaction1Service.addLog_NESTED(); transaction2Service.addUser_NESTED(); log.info("兩個事務。" + "logNested是外部事務的子事務" + "userNested是外部事務的子事務" + "外層事務有異常,回滾。兩個子事務都回滾"); throw new RuntimeException(); } /** * 不添加 */ @Transactional(propagation = Propagation.REQUIRED) public void NESTED_內層事務是外層事務的子事務_子事務有異常被外層感知到所有子事務都回滾(){ transaction1Service.addLog_NESTED(); transaction2Service.addUser_NESTED_Exception(); log.info("兩個事務。" + "logNested是外部事務的子事務" + "userNested是外部事務的子事務" + "userNested有異常,外層感知到異常。外層回滾,兩個子事務都回滾" ); } /** * log_nested添加成功 */ @Transactional(propagation = Propagation.REQUIRED) public void NESTED_內層方法是外層事務的子事務_子事務異常不被外層感知到有異常的子事務回滾_沒有異常的子事務提交(){ transaction1Service.addLog_NESTED(); try { transaction2Service.addUser_NESTED_Exception(); } catch (Exception e) { log.error("兩個事務。" + "logNested是外部事務的子事務" + "userNested是外部事務的子事務" + "userNested有異常,但是被catch捕獲到。外層感知不到異常。userNested回滾" + "logNested沒有異常。提交"); } } //------NESTED修飾的內層方法是外部事務的子事務,外層回滾。內層的都回滾。內層的獨立存在,互不干擾 //--------外層沒有事務的時候。也是新開事務,互相獨立。互不干擾。 /** * userRequires_new插入成功 */ @Transactional(propagation = Propagation.REQUIRED) public void 組合(){ transaction1Service.addLog_Required(); transaction2Service.addUser_REQUIRES_NEW(); transaction2Service.addUser_NESTED(); transaction2Service.addUser_NESTED_Exception(); log.info("兩個事務" + "logRequired加入到外層事務,外層捕獲到userNested返回的異常,回滾" + "userRequires_new新開事務。與外層沒有影響" + "userNested和userNestedException都是外層的子事務,外層捕獲到異常,進行回滾,兩個子事務也進行回滾"); } }
總結:
一:分析事務
看外層。如果外層沒有事務。那么去分析包含的方法中有沒有加上事務,有幾個方法加上了事務就開啟了幾個事務。這幾個事務是互相獨立,互不干擾的。
如果有事務。子事務中用REQUIRED 修飾的會加入到外層事務中。
子事務用REQUIRES_NEW 修飾的不會去搭理外層的事務。自己新開事務。
子事務用NESTED 修飾的是外層的子事務。如果外層事務回滾。外層事務下是所有子事務也回滾。
二:分析事務是否失效
2.1:拋出的是非運行時異常。
比如拋出Exception
但是可以加上rollbackFor
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
拋出RunTimeException。可以走事務,如果不想走事務的話可以加上noRollbackFor
@Transactional(noRollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
2.2:異常被catch掉
/** * 因為異常被吃掉,所以走不成事務,不會回滾 */ @Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException_Try(){ User user = User.builder() .account("張三REQUIRED運行時異常被cacth掉").status("ENABLE").build(); userMapper.insert(user); try { throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾"); } catch (Exception e) { log.error("異常被吃掉"); } }
public void 異常被吃掉(){ transaction2Service.addUser_REQUIRED_RuntimeException_Try(); }
異常被吃掉解決方案。
@Transactional(propagation = Propagation.REQUIRED) public void addUser_REQUIRED_RuntimeException_Try(){ User user = User.builder() .account("張三REQUIRED運行時異常被cacth掉").status("ENABLE").build(); userMapper.insert(user); try { throw new RuntimeException("RuntimeException是運行時異常,事務自動回滾"); } catch (Exception e) { log.error("異常被吃掉"); //解決方案一:在catch中加上 throw new RuntimeException() 把異常給拋出去。 //throw new RuntimeException(); //解決方案二: 在catch中手動回滾 //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } }