从源码看 MyBatis-Plus 与 Spring 的 DataSourceTransactionManager 有没有直接关联?
MyBatis-Plus(简称 MP)的事务管理用起来挺顺手,但它到底跟 Spring 的 DataSourceTransactionManager
有没有直接关系?这个问题得从源码里找答案。今天咱们就来扒一扒 MP 和 Spring 的源码,看看它们是怎么配合的,MP 是不是直接跟 DataSourceTransactionManager
杠上了。
1. 先说结论
先剧透一下:MyBatis-Plus 本身跟 DataSourceTransactionManager
没有直接关联 。MP 的事务管理完全依赖 Spring 的基础设施,核心是 Spring 的 @Transactional
注解和 DataSourceTransactionManager
。MP 只是个"借力打力"的角色,负责执行 SQL,而事务的控制权在 Spring 手里。
下面咱们一步步从源码里挖证据。
2. Spring 的事务管理:DataSourceTransactionManager
Spring 的事务管理核心是 PlatformTransactionManager
接口,DataSourceTransactionManager
是它的一个实现类,专门管 JDBC 数据源的事务。咱们先看看它咋工作的。
2.1 DataSourceTransactionManager 的核心逻辑
DataSourceTransactionManager
的源码在 doBegin
、doCommit
和 doRollback
方法里:
java
// org.springframework.jdbc.datasource.DataSourceTransactionManager
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 获取数据库连接
con = obtainDataSource().getConnection();
txObject.setConnectionHolder(new ConnectionHolder(con), true);
// 设置手动提交
con.setAutoCommit(false);
// 设置隔离级别、超时等
prepareConnectionForTransaction(con, definition);
} catch (SQLException ex) {
// 处理异常
}
}
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
con.commit(); // 提交事务
} catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
con.rollback(); // 回滚事务
} catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
这几段代码干了啥? - doBegin
:拿到数据源的 Connection
,关掉自动提交,开始事务。 - doCommit
:提交事务,把操作写到数据库。 - doRollback
:回滚事务,撤销操作。
这些操作都是针对 JDBC 的 Connection
,跟具体的 ORM 框架没直接关系。
2.2 Spring 的事务拦截
Spring 用 AOP 拦截带 @Transactional
的方法,核心类是 TransactionInterceptor
。它的 invoke
方法会调用 TransactionManager
:
java
// org.springframework.transaction.interceptor.TransactionInterceptor
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
TransactionAttributeSource tas = getTransactionAttributeSource();
TransactionAttribute txAttr = tas.getTransactionAttribute(invocation.getMethod(), targetClass);
PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 创建事务状态
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// 执行目标方法
retVal = invocation.proceed();
} catch (Throwable ex) {
// 出错了就回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
// 正常结束就提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
这里 tm
就是 DataSourceTransactionManager
,它负责事务的开启、提交和回滚。
3. MyBatis-Plus 的角色
MP 的事务管理完全靠 Spring,它自己没实现任何事务逻辑。MP 的核心是 SqlSession
和 Mapper
,这些都跟 MyBatis 一样,走的是 JDBC 的路子。咱们看看 MP 的源码。
3.1 MP 的 SqlSession
MP 用的是 MyBatis 的 SqlSession
,通过 SqlSessionFactory
创建。在 Spring 环境下,SqlSession
是由 SqlSessionTemplate
代理的:
java
// org.mybatis.spring.SqlSessionTemplate
public class SqlSessionTemplate implements SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory, ExecutorType.SIMPLE);
}
@Override
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.selectOne(statement, parameter);
}
}
SqlSessionTemplate
的构造方法会从 Spring 的 DataSource
获取连接,而这个连接已经被 DataSourceTransactionManager
控制了事务状态(比如 autoCommit=false
)。
3.2 MP 的 BaseMapper
MP 的 BaseMapper
是通用 CRUD 的核心,比如 insert
方法:
java
// com.baomidou.mybatisplus.core.mapper.BaseMapper
@InsertProvider(type = SqlProviderAdapter.class, method = "insert")
int insert(T entity);
这个方法最终会调用 MyBatis 的 SqlSession.insert
,而 SqlSession
的连接是从 Spring 的事务上下文里拿的。源码里没见到 MP 自己去碰 DataSourceTransactionManager
,它只管发 SQL。
4. 两者怎么配合?
从源码看,MP 和 DataSourceTransactionManager
的关系是间接的,中间靠 Spring 搭桥: 1. 你在 Service 方法上加 @Transactional
,Spring 的 TransactionInterceptor
拦截。 2. DataSourceTransactionManager
开启事务,控制 Connection
。 3. MP 的 Mapper
通过 SqlSession
执行 SQL,用的是同一个 Connection
。 4. 方法结束,Spring 根据执行结果提交或回滚。
MP 本身不直接调用 DataSourceTransactionManager
,甚至不知道它的存在。它只关心 SQL 执行,事务的生命周期全交给 Spring。
5. 代码验证
写个例子,看看实际效果:
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private OrderMapper orderMapper;
@Transactional(rollbackOn = Exception.class)
public void addUserAndOrder(String username, String orderName) {
User user = new User();
user.setUsername(username);
user.setAge(25);
baseMapper.insert(user);
Order order = new Order();
order.setUserId(user.getId());
order.setOrderName(orderName);
orderMapper.insert(order);
throw new RuntimeException("故意出错");
}
}
@SpringBootTest
class SourceTest {
@Autowired
private UserService userService;
@Test
void testTransaction() {
try {
userService.addUserAndOrder("张三", "手机");
} catch (Exception e) {
System.out.println("捕获异常: " + e.getMessage());
}
}
}
跑完后,数据库啥也没加。因为 DataSourceTransactionManager
检测到异常,调用了 Connection.rollback()
,MP 的两个 insert
都被撤销了。
6. 小结
从源码角度,MyBatis-Plus 跟 DataSourceTransactionManager
没直接关联。MP 只是个 SQL 执行工具,事务管理全靠 Spring 的 @Transactional
和 DataSourceTransactionManager
。MP 的 SqlSession
用的是 Spring 提供的事务性连接,事务的开关都在 Spring 手里。
所以,MP 的事务能力其实是"借来的",它跟 DataSourceTransactionManager
的关系是间接的,通过 Spring 的整合串起来。明白了这个,写代码时只要管好 @Transactional
,MP 自然会配合得妥妥的。
有啥想再深挖的没?比如某个具体方法的源码,我可以再展开讲讲!