多线程下导致@Transactional注解失效的解决办法

问题发生的原因:

1、为什么多线程情况下,事务注解会失效呢?

首先最核心的地方在于主线程和子线程都是不同的线程,他们之间都有自己的threadlocal,里面存放了数据库连接的核心操作。

在TransactionSynchronizationManager这个类里面可以看到。

存放了自己的local,然后resource就是存放了数据库连接的。

private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
    private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal("Current transaction name");
    private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal("Current transaction read-only status");
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal("Current transaction isolation level");
    private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal("Actual transaction active");

注解失效是因为这个注解里面的连接池只管理到这个主线程里面的,没有管到子线程,如果让子线程里面的数据库连接也使用主线程的那么此时如果主线程进行回滚,就能一并回滚子线程的事务

二、代码处理

第一步获取到主线程里面的数据库连接,然后子线程里面绑定主线程的连接, 使用完之后要记得解绑。

第二步使用异步线程,可以使用completableFuture,也可以用thread来处理,使用completableFuture就可以直接用join方法,把异常拿到主线程里面,让主线程去发现,往外抛,使用thread也是同理,setUncaughtExceptionHandler来把thread里面的异常拿到外面定义的变量,然后判断是否有发生异常,在手动抛出,这两个是一个意思。

核心要点就是,是用主线程的数据库连接,并且把子线程里面的异常拿到外面主线程去往外抛。

@Override
    @Transactional
    public void testTransactional() throws SQLException, ExecutionException, InterruptedException {
        // 主线程获取数据库连接资源 让这里的子线程也获取同一个数据库资源

        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);

        TCustomer tCustomer = new TCustomer();
        tCustomer.setSex(1);
        tCustomer.setAge(5);
        tCustomer.setPhone("12322222222");

        /*CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            // 子线程绑定
            TransactionSynchronizationManager.bindResource(dataSource, conHolder);

            tCustomer.setCname("异步线程插入的");
            tCustomer.setId(new Random().nextInt(1000));
            customerMapper.insert(tCustomer);
            //int a = 1 / 0;
            // 此处绑定完要解绑
            TransactionSynchronizationManager.unbindResource(dataSource);
        });*/
        final Throwable[] throwable = {null};
        // 通过普通线程来处理,然后核心也是把异常往主线程里面拿
        Thread thread = new Thread(() -> {
            // 子线程绑定
            TransactionSynchronizationManager.bindResource(dataSource, conHolder);

            tCustomer.setCname("异步线程插入的");
            tCustomer.setId(new Random().nextInt(1000));
            customerMapper.insert(tCustomer);
            int a = 1 / 0;
            // 此处绑定完要解绑
            TransactionSynchronizationManager.unbindResource(dataSource);
        });

        thread.start();
        thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println(e);
                throwable[0] = e;
            }
        });
        TCustomer tCustomer1 = new TCustomer();
        tCustomer1.setId(new Random().nextInt(100000));
        tCustomer1.setCname("主线程插入的");
        tCustomer1.setSex(1);
        tCustomer1.setAge(5);
        tCustomer1.setPhone("12322222222");
        customerMapper.insert(tCustomer1);
        /*jdbcTemplatel.update("insert into persion (id,name) values (?,?)",
                new Object[]{new Random().nextInt(1000), Thread.currentThread().getName()});*/
        // int a = 1 / 0;
        //future.join();
        // 让子线程先走完才能完成赋值操作
        thread.join();
        if (throwable[0]!= null){
            throw new RuntimeException(throwable[0]);
        }
    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值