java事务传播机制一文搞懂。

lanhy
发布于 2020-9-1 11:15
浏览
1收藏

使用:

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.

注意:有多个事务注解标注的方法相互调用的时候,一定要从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;
        }
    }
}

测试类
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.

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执行完没有发生异常,事务就回提交。

 

 

分类
标签
已于2020-9-2 18:14:02修改
1
收藏 1
回复
举报
1
1
回复
    相关推荐