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 + ");");
}
}
Spring使用编程式事务解决多线程事务共享问题
于 2024-03-10 16:16:26 首次发布
![](https://img-home.csdnimg.cn/images/20240709112858.png)