使用:
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; } }
注意:有多个事务注解标注的方法相互调用的时候,一定要从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); MapcountMap = 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执行完没有发生异常,事务就回提交。