前言
事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一。但除了八股文中需要熟读并背诵的那些个传播行为之外,背后的“为什么”和核心原理更为重要。
写这篇文章之前,我也翻过一些事务管理器原理介绍文章,但大多都是生硬的翻译源码,一个劲的给源码加注释。这种源码翻译的文章虽说有帮助,但对读者来说体验并不好,很容易陷入代码的一些细节里,并不能帮助读者快速的了解事务管理的全貌,以及设计思路。
本文会从设计角度,一步步的剖析 Spring 事务管理的设计思路(都会设计事务管理器了,还能不不会用么?)
为什么需要事务管理?
先看看如果没有事务管理器的话,如果想让多个操作(方法/类)处在一个事务里应该怎么做:
// 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();
}
或者用 ThreadLocal 存储 Connection?
static ThreadLocal<Connection> 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();
}
还是有点恶心,再抽象一下?将绑定 Connection 的操作提取为公共方法:
static ThreadLocal<Connection> 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();
}
现在看起来好点了,不过我有一个新的需求:想让 methodB 独立一个新事务,单独提交和回滚,不影响 methodA
这……可就有点难搞了,ThreadLocal 中已经绑定了一个 Connection,再新事务的话就不好办了
那如果再复杂点呢,methodB 中需要调用 methodC,methodC 也需要一个独立事务……
而且,每次 bind/unbind 的操作也有点太傻了,万一哪个方法忘了写 unbind ,最后来一个连接泄露那不是完蛋了!
好在 Spring 提供了事务管理器,帮我们解决了这一系列痛点。
Spring 事务管理解决了什么问题?
Spring 提供的事务管理可以帮我们管理事务相关的资源,比如 JDBC 的 Connection、Hibernate 的 Session、Mybatis 的 SqlSession。如说上面的 Connection 绑定到 ThreadLocal 来解决共享一个事务的这种方式,Spring 事务管理就已经帮我们做好了。
还可以帮我们处理复杂场景下的嵌套事务,比如前面说到的 methodB/methodC 独立事务。
什么是嵌套事务?
还是拿上面的例子来说, methodA 中调用了 methodB,两个方法都有对数据库的操作,而且都需要事务:
// MethodA:
public void methodA(){
int updated = connection.prepareStatement().executeUpdate();
methodB();
// ...
}
// MethodB:
public void methodB(){
// ...
}
**这种多个方法调用链中都有事务的场景,就是嵌套事务。**不过要注意的是,并不是说多个方法使用一个事务才叫嵌套,哪怕是不同的事务,只要在这