来源:京东云开发者
// MethodA:
public void methodA(){
Connection connection = acquireConnection();
try{
int updated = connection.prepareStatement().executeUpdate();
methodB(connection);
connection.commit();
}catch (Exception e){
rollback(connection);
}finally {
releaseConnection(connection);
}
}
// MethodB:
public void methodB(Connection connection){
int updated = connection.prepareStatement().executeUpdate();
}
static ThreadLocal
connHolder = new ThreadLocal<>();
// MethodA:
public void methodA(){
Connection connection = acquireConnection();
connHolder.set(connection);
try{
int updated = connection.prepareStatement().executeUpdate();
methodB();
connection.commit();
}catch (Exception e){
rollback(connection);
}finally {
releaseConnection(connection);
connHolder.remove();
}
}
// MethodB:
public void methodB(){
Connection connection = connHolder.get();
int updated = connection.prepareStatement().executeUpdate();
}
static ThreadLocal
connHolder = new ThreadLocal<>();
private void bindConnection(){
Connection connection = acquireConnection();
connHolder.set(connection);
}
private void unbindConnection(){
releaseConnection(connection);
connHolder.remove();
}
// MethodA:
public void methodA(){
try{
bindConnection();
int updated = connection.prepareStatement().executeUpdate();
methoB();
connection.commit();
}catch (Exception e){
rollback(connection);
}finally {
unbindConnection();
}
}
// MethodB:
public void methodB(){
Connection connection = connHolder.get();
int updated = connection.prepareStatement().executeUpdate();
}
什么是嵌套事务?
// MethodA:
public void methodA(){
int updated = connection.prepareStatement().executeUpdate();
methodB();
// ...
}
// MethodB:
public void methodB(){
// ...
}
什么是事务传播行为?
有哪些事务传播行为?
优先使用当前事务 不使用当前事务,新建事务 不使用任何事务
看个栗子
@Transactional
public void methodA(){
jdbcTemplate.batchUpdate(updateSql, params);
methodB();
}
@Transactional
public void methodB(){
jdbcTemplate.batchUpdate(updateSql, params);
}
@Transactional
public void methodA(){
jdbcTemplate.batchUpdate(updateSql, params);
methodB();
}
// 传播行为配置为 - 方式2,不使用当前事务,独立一个新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB(){
jdbcTemplate.batchUpdate(updateSql, params);
}
怎么回滚?
// 传播行为配置为 - 方式2,不使用当前事务,独立一个新事务
// 指定 Exception 也不会滚
@Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class)
public void methodB(){
jdbcTemplate.batchUpdate(updateSql, params);
}
每个事务/连接使用不同配置
@Transactional
public void methodA(){
jdbcTemplate.batchUpdate(updateSql, params);
methodB();
}
// 传播行为配置为 - 方式2,不使用当前事务,独立一个新事务
// 这个事务/连接中使用 RC 隔离级别,而不是默认的 RR
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_UNCOMMITTED)
public void methodB(){
jdbcTemplate.batchUpdate(updateSql, params);
}
功能总结
连接/资源管理 - 无需手动获取资源、共享资源、释放资源 嵌套事务的支持 - 支持嵌套事务中使用不同的资源策略、回滚策略 每个事务/连接使用不同的配置
三、事务管理器(TransactionManager)模型
获取事务资源,资源可以是任意的,比如jdbc connection/hibernate mybatis session之类,然后绑定并存储 提交事务 - 提交指定的事务资源 回滚事务 - 回滚指定的事务资源
interface PlatformTransactionManager{ // 获取事务资源,资源可以是任意的,比如jdbc connection/hibernate mybatis session之类 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交事务 void commit(TransactionStatus status) throws TransactionException; // 回滚事务 void rollback(TransactionStatus status) throws TransactionException;}
事务定义 - TransactionDefinition
比如配置了使用新事务,那么在获取事务资源时就需要创建一个新的,而不是已有的 比如配置了隔离级别,那么在首次创建资源(Connection)时,就需要给 Connection 设置 propagation 比如配置了只读属性,那么在首次创建资源(Connection)时,就需要给 Connection 设置 readOnly
事务状态 - TransactionStatus
获取事务资源
当前有事务 - 根据不同传播行为处理不同 当前没事务,但需要开启新事务 彻底不用事务 - 这个很少用
public final TransactionStatus getTransaction(TransactionDefinition definition) { //创建事务资源 - 比如 Connection Object transaction = doGetTransaction(); if (isExistingTransaction(transaction)) { // 处理当前已有事务的场景 return handleExistingTransaction(def, transaction, debugEnabled); }else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED){ // 开启新事务 return startTransaction(def, transaction, debugEnabled, suspendedResources); }else { // 彻底不用事务 } // ...}
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled, SuspendedResourcesHolder suspendedResources) { // 创建事务 DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); // 开启事务(beginTx或者setAutoCommit之类的操作) // 然后将事务资源绑定到事务资源管理器 TransactionSynchronizationManager doBegin(transaction, definition);
挂起(Suspend)和恢复(Resume)
整个挂起 - 恢复的流程,如下图所示:


提交事务
当前是新事务才提交 处理挂起资源
每经过一次事务嵌套,都会创建一个新的 TransactionStatus,这个事务状态里会记录当前是否是新事务。如果多个子方法都使用一个事务资源,那么除了第一个创建事务资源的 TransactionStatus 之外,其他都不是新事务。

这里再解释下,为什么新事务才需要提交,而已经有事务却什么都不用做:
回滚事务
如果是新事务才回滚,原因上面已经介绍过了 如果不是新事务则只设置回滚标记 处理挂起资源
XML AOP 的事务管理 - 比较古老现在用的不多 注解版本的事务管理 - @Transactional TransactionTemplate - 手动挡的事务管理,也称编程式事务管理
自动挡
public Object invoke(MethodInvocation invocation) throws Throwable { // 获取事务资源 Object transaction = transactionManager.getTransaction(txAttr); Object retVal; try { // 执行业务代码 retVal = invocation.proceedWithInvocation(); // 提交事务 transactionManager.commit(txStatus); } catch (Throwable ex){ // 先判断异常回滚策略,然后调用事务管理器的 rollback rollbackOn(ex, txStatus); } }
手动挡
publicT execute(TransactionCallback action) throws TransactionException { // 获取事务资源 TransactionStatus status = this.transactionManager.getTransaction(this); T result; try { // 执行 callback 业务代码 result = action.doInTransaction(status); } catch (Throwable ex) { // 调用事务管理器的 rollback rollbackOnException(status, ex); } 提交事务 this.transactionManager.commit(status); }}
为什么有这么方便的自动挡,还要手动挡?
Integer rows = new TransactionTemplate((PlatformTransactionManager) transactionManager,
new DefaultTransactionDefinition(TransactionDefinition.ISOLATION_READ_UNCOMMITTED))
.execute(new TransactionCallback
() {
public Integer doInTransaction(TransactionStatus status) {
// update 0
int rows0 = jdbcTemplate.update(...);
// update 1
int rows1 = jdbcTemplate.update(...);
return rows0 + rows1;
}
});
Integer rows2 = new TransactionTemplate((PlatformTransactionManager) transactionManager,
new DefaultTransactionDefinition(TransactionDefinition.ISOLATION_READ_UNCOMMITTED))
.execute(new TransactionCallback
() {
public Integer doInTransaction(TransactionStatus status) {
// update 2
int rows2 = jdbcTemplate.update(...);
return rows2;
}
});
手自一体可以吗?
@Transactionalpublic void methodA(){ // update 1 jdbcTemplate.update(...); new TransactionTemplate((PlatformTransactionManager) transactionManager, new DefaultTransactionDefinition(TransactionDefinition.ISOLATION_READ_UNCOMMITTED)) .execute(new TransactionCallback() { @Override public Integer doInTransaction(TransactionStatus status) { // update 2 int rows2 = jdbcTemplate.update(...); return rows2; } }); }
五、总结

本文为了方便理解,省略了大量的非关键实现细节,可能会导致有部分描述不严谨的地方,如有问题欢迎评论区留言。