Spring使用编程式事务解决多线程事务共享问题

文章详细描述了一个在Spring框架下如何在多个线程中通过注入和事务管理器实现使用同一个JDBC连接保持事务一致性的Service类。作者强调了事务定义、获取事务状态和资源管理的重要性。
摘要由CSDN通过智能技术生成
package luck.spring.tx;

/**
 * 定义Service类
 */
@Service
public class ThreadUserService {

    /**
     * 注入当前类的对象,可能是代理,也可以不是
     * Lazy是必须得,因为自己注入自己
     */
    @Autowired
    @Lazy
    private ThreadUserService userService;

    /**
     * 操作数据库的
     */
    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 操作事务的失误管理器
     */
    @Autowired
    private DataSourceTransactionManager transactionManager;

    /**
     * 需要实际调用的目标方法
     * 这里设计到太多的细节,只是给出了一个简单的解决方法,在不同的线程中使用相同的jdbc连接,共享一个事务
     */
    public void test1() {
        // 获取事务定义,设置对事务设置属性,例如传播级别...
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        // 获取事务,这里是核心,获取jdbc链接,开启事务,绑定jdbc链接到当前线程
        TransactionStatus status = transactionManager.getTransaction(definition);
        try {
            // 为什么使用this,因为没有使用声明式事务,不需要使用代理对象调用
            this.test("test1");
        } catch (Exception e) {
            // 因为test方法都出现异常了,下面就无需执行,直接回滚
            transactionManager.rollback(status);
            throw e;
        }
        // test方法执行正常,目前还没提交,要等到下面这个线程执行完毕,统一提交
        DataSource dataSource = transactionManager.getDataSource();
        // 这个也是核心,是当前jdbc链接绑定到线程的方法
        // 因为上面已经开启了事务,将链接绑定了当前线程,key为当前数据源,所以我们在这里线获取到这个jdbc链接,准备给下面这个线程使用
        // 最终达到两个不同的线程使用相同的jdbc链接,从而达到事务一致性
        ConnectionHolder connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        // 开启新线程
        new Thread(() -> {
            // 将上一个线程获取到的jdbc链接绑定到当前线程,两个线程共用同一个链接,共享同一个事务
            TransactionSynchronizationManager.bindResource(dataSource, connectionHolder);
            // 获取当前新线程所属的事务信息,内部会判断当前线程有没有绑定jdbc链接,如果绑定了,就会使用当前链接
            // 因为我们上面给当前线程绑定了之前线程获取到的链接,或者这里使用的是原来的链接
            // 这个到底是不是使用同一个事务,还是新建事务,取决于definition的定义,根据definition定义的传播行为来决定
            // 当definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW)的时候
            // 就算给当前线程绑定了上一线程的事务连接,那么也会将上一个事务连接进行暂停,然后创建一个新的事务执行
            // 这里只是给了一种多线程共享同一个事务的解决方案,如果两个业务方法确定共享同一个事务,那么
            // 当前线程的transactionManager.getTransaction(definition)获取事务的方法都可以省去
            // 因为他们最终操作的都是同一个事务,而这个事务是我们自己控制的,什么时候提交回滚都取决于我们,只需要能获取到一个已存在的事务就行
            // 不懂原理的可以看我的事务相关原理解读
            TransactionStatus status1 = transactionManager.getTransaction(definition);
            try {
                // 调用操作方法
                this.test("thread-test1");
                // 没有异常,提交事务
                transactionManager.commit(status1);
            } catch (Exception e) {
                // 如果有异常,回滚事务
                transactionManager.rollback(status1);
            }
        }).start();
    }

    public void test(String name) {
        if (name.length() > 5) {
            System.out.println(1 / 0);
        }
        InheritableThreadLocal<ConnectionHolder> local = new InheritableThreadLocal<>();
        String string = "\"" + RandomUtil.randomString(5) + "\"";
        System.out.println(Thread.currentThread().getName() + " is " + name + " random str is " + string);
        jdbcTemplate.execute("insert into jdbc (id, name) values (default," + string + ");");
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值