使用:
1、引入spring的jdbc、資料庫驅動,資料源
2、配置資料源,注入JdbcTemplate,啟用事務管理,注入DataSourceTransactionManager
3、傳播機制
@see Propagation#REQUIRED 支持當前事務,如果沒有則新建一個事務,
例:a方法呼叫b方法,如果a方法有事務,則b加入a事務,如果a沒有,則b新建一個事務
@see Propagation#SUPPORTS 支持當前事務,如果沒有則以非事務運行
例:a方法呼叫b方法,如果a方法有事務,則b加入a事務,如果a沒有,則b以非事務運行
@see Propagation#MANDATORY 支持當前事務,如果沒有則拋出例外
例:a方法呼叫b方法,如果a方法有事務,則b加入a事務,如果a沒有,則b拋出例外
@see Propagation#REQUIRES_NEW 創建一個新的事務,如果當前有事務,則掛起
例:a方法呼叫b方法,不論a有沒有事務,b都會新建一個事務
@see Propagation#NOT_SUPPORTED 不支持事務,如果當前有事務則掛起
例:a方法呼叫b方法,不論a有沒有事務,b都會以非事務方式運行
@see Propagation#NEVER 不支持事務,如果有則拋出例外
例:a方法呼叫b方法,如果a有事務,b會拋出例外
@see Propagation#NESTED 嵌套事務
例:a方法呼叫b方法,如果a有事務,b會開啟一個內嵌事務,如果a沒有事務,則b開啟一個新的事務
4、代碼測驗
服務類:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.Random; /** * @author liangjunhui * @date Created in 2020-08-12 11:25 */ @Service public class UsersService { @Autowired private JdbcTemplate jdbcTemplate; @Autowired UsersService usersService; private int num = 9; @Transactional(rollbackFor = Exception.class,timeout = 10) public void requiredA(int a, int b, Propagation propagation) { try { insertUser(a); } finally { invoke(b, propagation); } } @Transactional(rollbackFor = Exception.class) public void requiredACatchB(int a, int b, Propagation propagation) { try { insertUser(a); } finally { try { invoke(b, propagation); } catch (Exception e) { } } } public void invoke(int b, Propagation propagation) { switch (propagation) { case REQUIRED: usersService.requiredB(b); break; case SUPPORTS: usersService.supportsB(b); break; case MANDATORY: usersService.mandatoryB(b); break; case REQUIRES_NEW: usersService.requiresNewB(b); break; case NOT_SUPPORTED: usersService.notSupportedB(b); break; case NEVER: usersService.neverB(b); break; case NESTED: usersService.nestedB(b); break; default: break; } } @Transactional(rollbackFor = Exception.class) public void requiredB(int b) { insertUser(b); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.SUPPORTS) public void supportsB(int b) { insertUser(b); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.MANDATORY) public void mandatoryB(int b) { insertUser(b); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public void requiresNewB(int b) { insertUser(b); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.NOT_SUPPORTED) public void notSupportedB(int b) { insertUser(b); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.NEVER) public void neverB(int b) { insertUser(b); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED) public void nestedB(int b) { insertUser(b); } /** * 當i為0的時候,會發生例外 * @param i * @return */ private int insertUser(int i) { String methodName = Thread.currentThread().getStackTrace()[2].getMethodName(); String name = methodName + new Random().nextInt(); int update = jdbcTemplate.update("insert into users(`userName`,`passWord`) values (?,?)", new Object[]{name, "123456"}); int res = num / i; return update; } }service
注意:有多個事務注解標注的方法相互呼叫的時候,一定要從IOC容器中取物件,否則不生效,原因是apc默認創建的物件呼叫目標方法是通過實體化初始化之后的bean,這個bean是未代理增強的,創建代理是BeanPostProcessor實作類負責的,通過初始化的后置處理器包裝,然后將包裝好的bean放入IOC容器中,這個代理物件持有未代理之前的參考,所以說要從容器中取,
測驗類:
/** * @author liangjunhui * @date Created in 2020-08-12 10:58 */ public class TestTx extends BaseTest { /** * 測驗所有的事務傳播,該情況呼叫方不處理被呼叫方的例外 * 測驗結果: * ----------------------測驗REQUIRED開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功0次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * 當前無事務,失敗共執行1次,成功0次 * 當前無事務,成功共執行1次,成功1次 * ----------------------測驗REQUIRED結束------------------------- * ----------------------測驗SUPPORTS開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功0次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * 當前無事務,失敗共執行1次,成功1次 * 當前無事務,成功共執行1次,成功1次 * ----------------------測驗SUPPORTS結束------------------------- * ----------------------測驗MANDATORY開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功0次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * 發生例外:No existing transaction found for transaction marked with propagation 'mandatory' * 當前無事務,失敗共執行1次,成功0次 * 發生例外:No existing transaction found for transaction marked with propagation 'mandatory' * 當前無事務,成功共執行1次,成功0次 * ----------------------測驗MANDATORY結束------------------------- * ----------------------測驗REQUIRES_NEW開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功0次 * 當前有事務,a失敗,b成功共執行2次,成功1次 * 當前無事務,失敗共執行1次,成功0次 * 當前無事務,成功共執行1次,成功1次 * ----------------------測驗REQUIRES_NEW結束------------------------- * ----------------------測驗NOT_SUPPORTED開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功1次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功1次 * 當前有事務,a失敗,b成功共執行2次,成功1次 * 當前無事務,失敗共執行1次,成功1次 * 當前無事務,成功共執行1次,成功1次 * ----------------------測驗NOT_SUPPORTED結束------------------------- * ----------------------測驗NEVER開始------------------------- * 發生例外:Existing transaction found for transaction marked with propagation 'never' * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 發生例外:Existing transaction found for transaction marked with propagation 'never' * 當前有事務,a成功,b成功共執行2次,成功0次 * 發生例外:Existing transaction found for transaction marked with propagation 'never' * 當前有事務,a成功,b失敗共執行2次,成功0次 * 發生例外:Existing transaction found for transaction marked with propagation 'never' * 當前有事務,a失敗,b成功共執行2次,成功0次 * 當前無事務,失敗共執行1次,成功1次 * 當前無事務,成功共執行1次,成功1次 * ----------------------測驗NEVER結束------------------------- * ----------------------測驗NESTED開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功0次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * 當前無事務,失敗共執行1次,成功0次 * 當前無事務,成功共執行1次,成功1次 * ----------------------測驗NESTED結束------------------------- */ @Test public void testTx(){ for (Propagation propagation:Propagation.values()){ System.out.println("----------------------測驗"+propagation.name()+"開始-------------------------"); testTxCommon(propagation); System.out.println("----------------------測驗"+propagation.name()+"結束-------------------------"); } } /** * 測驗所有的事務傳播,該情況呼叫方處理被呼叫方的例外 * 測驗結果: * ----------------------測驗REQUIRED開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 發生例外:Transaction rolled back because it has been marked as rollback-only * 當前有事務,a成功,b失敗共執行2次,成功0次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * ----------------------測驗REQUIRED結束------------------------- * ----------------------測驗SUPPORTS開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 發生例外:Transaction rolled back because it has been marked as rollback-only * 當前有事務,a成功,b失敗共執行2次,成功0次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * ----------------------測驗SUPPORTS結束------------------------- * ----------------------測驗MANDATORY開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 發生例外:Transaction rolled back because it has been marked as rollback-only * 當前有事務,a成功,b失敗共執行2次,成功0次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * ----------------------測驗MANDATORY結束------------------------- * ----------------------測驗REQUIRES_NEW開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功1次 * 當前有事務,a失敗,b成功共執行2次,成功1次 * ----------------------測驗REQUIRES_NEW結束------------------------- * ----------------------測驗NOT_SUPPORTED開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功1次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功2次 * 當前有事務,a失敗,b成功共執行2次,成功1次 * ----------------------測驗NOT_SUPPORTED結束------------------------- * ----------------------測驗NEVER開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功1次 * 當前有事務,a成功,b失敗共執行2次,成功1次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * ----------------------測驗NEVER結束------------------------- * ----------------------測驗NESTED開始------------------------- * 當前有事務,a失敗,b失敗共執行2次,成功0次 * 當前有事務,a成功,b成功共執行2次,成功2次 * 當前有事務,a成功,b失敗共執行2次,成功1次 * 當前有事務,a失敗,b成功共執行2次,成功0次 * ----------------------測驗NESTED結束------------------------- */ @Test public void testTxCacheB(){ for (Propagation propagation:Propagation.values()){ System.out.println("----------------------測驗"+propagation.name()+"開始-------------------------"); testTxCommonCacheB(propagation); System.out.println("----------------------測驗"+propagation.name()+"結束-------------------------"); } } private void testTxCommon(Propagation propagation) { AllStatus[] values = AllStatus.values(); for (AllStatus status : values) { String[] strArr = status.getStatus().split(""); int before = testCount(); int a = Integer.parseInt(strArr[0]); try { if (strArr.length > 1) { int b = Integer.parseInt(strArr[1]); usersService.requiredA(a, b, propagation); } else { usersService.invoke(a, propagation); } }catch (ArithmeticException ae){ // e.printStackTrace(); }catch (Exception e) { System.out.println("發生例外:"+e.getMessage()); } int after = testCount(); System.out.println(status.getMsg() + "共執行" + strArr.length + "次,成功" + (after - before) + "次"); } } private void testTxCommonCacheB(Propagation propagation) { AllStatus[] values = AllStatus.values(); for (AllStatus status : values) { String[] strArr = status.getStatus().split(""); int before = testCount(); int a = Integer.parseInt(strArr[0]); try { if (strArr.length > 1) { int b = Integer.parseInt(strArr[1]); usersService.requiredACatchB(a, b, propagation); } else { continue; } }catch (ArithmeticException ae){ // e.printStackTrace(); }catch (Exception e) { System.out.println("發生例外:"+e.getMessage()); } int after = testCount(); System.out.println(status.getMsg() + "共執行" + strArr.length + "次,成功" + (after - before) + "次"); } } public int testCount() { JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class); Map<String, Object> countMap = jdbcTemplate.queryForMap("select count(1) as count from users"); return Integer.parseInt(countMap.get("count").toString()); } @Before public void before() { getContext(TxConfig.class); usersService = context.getBean(UsersService.class); } private UsersService usersService; enum AllStatus { ALL_FAILE("00", "當前有事務,a失敗,b失敗"), ALL_SUCCESS("11", "當前有事務,a成功,b成功"), SUCCESS_FAILE("10", "當前有事務,a成功,b失敗"), FAILE_SUCCESS("01", "當前有事務,a失敗,b成功"), FAILE("0", "當前無事務,失敗"), SUCCESS("1", "當前無事務,成功"),; private String status; private String msg; AllStatus(String status, String msg) { this.status = status; this.msg = msg; } public String getStatus() { return status; } public String getMsg() { return msg; } } }測驗類
5、REQUIRED、REQUIRES_NEW、NESTED 之間的區別:
REQUIRED是a,b公用一個事務,a、b任一一個發生例外,事務都會回滾,即使是a未發生例外且處理了b的例外;
NESTED是a、b為父子事務,如果b發生了例外,a處理了b的例外,這樣只有b會回滾,如果a發生了例外,則回滾,
父影響子,子不影響父,
REQUIREs_NEW是a、b只會影響到自己,即只要a或b執行完沒有發生例外,事務就回提交,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/69361.html
標籤:Java
上一篇:Mybatis例外-java.lang.IllegalArgumentException: invalid comparison:java.util.Date
