← 返回题目列表

MyBatis 自己的事务管理是怎样的?和 Spring 的 @Transactional 有什么关系?

中等 第 22 / 24 题 更新于 2026/07/28
事务管理JDBC事务MANAGEDSpring事务

简化版

MyBatis 自己有一套事务管理,由 TransactionFactory + Transaction 抽象,两种内置类型:① JDBC——MyBatis 直接用 JDBC 的 Connection 管理事务(connection.commit()/rollback()),自己控制提交回滚;② MANAGED——MyBatis「不管事务」,把事务交给外部容器(如应用服务器 JTA)管理,它自己什么都不做。原生 MyBatis 里,事务是通过 SqlSession 手动控制的:openSession() 默认不自动提交,你要自己 session.commit()/rollback(),用的就是 JDBC 事务类型。但在和 Spring 整合后,MyBatis 自己的事务管理「让位」给 Spring——mybatis-spring 用一个 SpringManagedTransaction,事务的开启/提交/回滚全交给 Spring 的 @TransactionalDataSourceTransactionManager)统一管理,MyBatis 只负责用「Spring 事务绑定的那个连接」执行 SQL。所以实际项目里你几乎不直接用 MyBatis 的事务,而是用 Spring 的 @Transactional——MyBatis 的事务管理主要是「原生使用」和「理解底层」时才涉及。

详细版

两种内置事务类型

类型谁管事务怎么工作用在哪
JDBCMyBatis 自己直接用 Connection.commit/rollback原生 MyBatis、独立使用
MANAGED外部容器MyBatis 不提交/不回滚(交给容器)JTA/应用服务器管理事务
(SpringManaged)Spring跟随 Spring 事务同步mybatis-spring 整合后
// 原生 MyBatis:手动控制事务(JDBC 类型)
SqlSession session = factory.openSession();   // 默认不自动提交
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    mapper.update(...);
    mapper.update(...);
    session.commit();        // 手动提交(两个操作在一个事务里)
} catch (Exception e) {
    session.rollback();      // 出错回滚
} finally {
    session.close();
}

// openSession(true):自动提交(每条 SQL 独立提交,通常不用于业务)
// Spring 整合后:用 @Transactional,不碰 MyBatis 的事务 API
@Transactional
public void transfer() {
    userMapper.update(...);   // Spring 管理事务,两个操作同事务
    userMapper.update(...);   // @Transactional 提交/回滚
}

⚠️ 一个高频细节:原生 openSession() 默认「不自动提交」——你不 commit(),增删改就不会真正写库。很多人第一次用原生 MyBatis 做 insert,发现「代码跑了、没报错,但数据库里没数据」,就是忘了 session.commit()(默认手动提交模式,操作只在事务里、没提交就等于没生效,close() 时还会回滚)。而 openSession(true) 是自动提交,但自动提交不适合业务(每条 SQL 独立提交,无法保证多个操作的原子性)。到了 Spring 里就没这个坑了——@Transactional 帮你管提交回滚。所以要记住:原生 MyBatis 手动提交要自己 commit;Spring 里交给 @Transactional,两者别混着用(在 Spring 事务里又手动 commit 会出问题)。

完整版教学

一、MyBatis 的事务抽象

先理解 MyBatis 自己的事务抽象结构:

MyBatis 的事务抽象(两个接口):
  TransactionFactory:创建 Transaction 的工厂
  Transaction:事务对象,提供
    getConnection():拿数据库连接
    commit():提交
    rollback():回滚
    close():关闭

两种内置实现(在 mybatis-config.xml 的 <transactionManager> 配):
  <transactionManager type="JDBC"/>     → JdbcTransactionFactory
  <transactionManager type="MANAGED"/>  → ManagedTransactionFactory

SqlSession 内部持有一个 Transaction:
  你调 session.commit() → 实际调 transaction.commit()
  → 具体怎么提交,看 Transaction 是哪种实现

所以 MyBatis 把"事务怎么管"抽象成 Transaction 接口
  → 不同实现(JDBC/MANAGED/Spring)用不同方式管事务

MyBatis 的事务抽象是 TransactionFactory(创建工厂)+ Transaction(事务对象,提供 getConnection/commit/rollback/close。两种内置实现:JDBCJdbcTransactionFactory)、MANAGEDManagedTransactionFactory),在配置里选。SqlSession 内部持有一个 Transactionsession.commit() 实际调 transaction.commit()——具体怎么提交看是哪种实现。理解「MyBatis 事务抽象:TransactionFactory+Transaction(getConnection/commit/rollback)、两种内置 JDBC/MANAGED、SqlSession 持有 Transaction、commit 委托给它」,就理解了事务抽象结构。

二、JDBC 事务类型

JDBC 类型是「MyBatis 自己用 JDBC 连接管理事务」:

JDBC 事务类型(JdbcTransaction):
  直接用底层的 java.sql.Connection 管理事务:
    - 拿到 Connection
    - 设 autoCommit=false(关闭自动提交,开启事务)
    - commit() → connection.commit()
    - rollback() → connection.rollback()

所以 JDBC 类型 = "MyBatis 自己用 Connection 的事务能力"
  它掌控着这个连接的提交/回滚

用在哪:
  - 独立使用 MyBatis(不整合 Spring)时的默认选择
  - 你通过 SqlSession.commit()/rollback() 控制事务

一个 SqlSession = 一个 Transaction = 一个 Connection = 一个事务
  这就是为什么"一个 SqlSession 里的多个操作在同一个事务"
  (它们共用一个连接,一起 commit 或 rollback)

JDBC 类型是「MyBatis 自己用 Connection 管理事务」——拿到 Connection、设 autoCommit=falsecommit/rollback 直接调 connection 的对应方法。它是独立使用 MyBatis 时的默认选择,你通过 SqlSession.commit/rollback 控制。一个 SqlSession = 一个 Transaction = 一个 Connection = 一个事务——所以一个 SqlSession 里的多个操作在同一事务(共用一个连接,一起 commit/rollback)。理解「JDBC 类型=MyBatis 用 Connection 管事务(autoCommit=false,commit 调 connection)、独立使用默认、一个 SqlSession=一个连接=一个事务」,就理解了 JDBC 事务类型。

三、MANAGED 事务类型

MANAGED 类型是「MyBatis 不管事务,交给外部容器」:

MANAGED 事务类型(ManagedTransaction):
  MyBatis 自己"什么都不做"——
    commit():空操作(不提交)
    rollback():空操作(不回滚)
    → 把事务交给"外部容器"管理

用在哪:
  - 应用运行在 JEE 应用服务器里,用容器的 JTA 事务
  - 事务由容器(如 WebLogic、WebSphere 的 JTA)统一管理
  - MyBatis 只负责执行 SQL,事务边界由容器控制

为什么 MyBatis 要"什么都不做":
  因为事务由更上层的容器管了(JTA 全局事务、可能跨多个资源)
  如果 MyBatis 自己也 commit → 和容器的事务管理冲突
  → 所以 MANAGED 让 MyBatis 退出事务管理,避免冲突

现在用得少:
  纯 JEE 容器 JTA 的场景少了;
  Spring 整合是主流(下一节),Spring 有自己的方式

MANAGED 类型是「MyBatis 不管事务,交给外部容器」——commit/rollback 都是空操作,事务交给外部容器(如 JEE 应用服务器的 JTA)管理。MyBatis 只执行 SQL、事务边界由容器控制。为什么「什么都不做」——因为事务由更上层容器管了(可能是跨多资源的 JTA 全局事务),MyBatis 自己 commit 会冲突。现在用得少(纯 JEE 容器 JTA 场景少,Spring 整合是主流)。理解「MANAGED 类型=MyBatis 不管事务(commit/rollback 空操作)交给外部容器(JTA)、避免和容器事务冲突、现在用得少」,就理解了 MANAGED 事务类型。

四、Spring 整合:SpringManagedTransaction

和 Spring 整合后,MyBatis 事务「让位」给 Spring:

mybatis-spring 引入 SpringManagedTransaction:
  它是 Transaction 的一个特殊实现,行为是"跟随 Spring 事务":
    - getConnection():从 Spring 的事务同步管理器
      (DataSourceUtils.getConnection) 拿"当前 Spring 事务绑定的连接"
    - commit()/rollback():★通常"不主动做"——
      因为事务的提交/回滚由 Spring 的事务管理器统一控制
      (@Transactional 结束时,Spring 提交/回滚那个连接)

效果:
  MyBatis 用的连接 = Spring 事务绑定的连接
  MyBatis 不自己提交/回滚 → 全听 Spring 的 @Transactional
  → MyBatis 的操作天然纳入 Spring 事务

所以整合后:
  - 你用 @Transactional 声明事务边界
  - MyBatis 只管用"Spring 给的连接"执行 SQL
  - 提交/回滚由 Spring 统一做
  → MyBatis 自己的 JDBC/MANAGED 都不用了,用 SpringManagedTransaction

和 Spring 整合后,mybatis-springSpringManagedTransaction——它「跟随 Spring 事务」:getConnection 从 Spring 事务同步管理器拿「当前 Spring 事务绑定的连接」,commit/rollback 通常不主动做(由 Spring 事务管理器统一控制)。效果是 MyBatis 用 Spring 事务绑定的连接、自己不提交回滚、全听 @Transactional——MyBatis 操作天然纳入 Spring 事务。所以整合后用 @Transactional 声明边界、MyBatis 只管执行 SQL、Spring 统一提交回滚(见 spring-integration 题)。理解「Spring 整合用 SpringManagedTransaction 跟随 Spring 事务:用 Spring 绑定的连接、自己不提交回滚、听 @Transactional、MyBatis 操作纳入 Spring 事务」,就理解了整合后的事务管理。

五、原生使用的坑:默认不自动提交

原生使用 MyBatis 有个高频坑——openSession() 默认不自动提交:

openSession() 的两种模式:
  openSession()      → autoCommit=false(手动提交,默认)
  openSession(true)  → autoCommit=true(自动提交)

手动提交模式(默认)的坑:
  session.getMapper(UserMapper.class).insert(user);
  // 忘了 session.commit()!
  session.close();
  → 结果:数据库里没有这条数据!
  原因:手动提交模式下,操作只在事务里、没 commit 就没生效
        close() 时如果没 commit,会 rollback(丢弃未提交的操作)

所以原生 MyBatis 做增删改,必须 commit:
  try (SqlSession s = factory.openSession()) {
      s.getMapper(...).insert(...);
      s.commit();   // ★ 别忘了
  }

自动提交模式(openSession(true)):
  每条 SQL 自动提交,但不适合业务——
  多个操作无法组成一个原子事务(一条成功一条失败无法一起回滚)

到了 Spring:@Transactional 帮你管,没这个坑

原生使用的高频坑:openSession() 默认不自动提交(手动提交模式)——做 insert忘了 session.commit(),会「代码跑了没报错、但数据库没数据」(未提交的操作在 close() 时被回滚)。所以原生 MyBatis 增删改必须 commitopenSession(true) 是自动提交但不适合业务(每条 SQL 独立提交、无法保证多操作原子性)。到 Spring 里 @Transactional 帮你管、没这个坑。理解「openSession() 默认手动提交、忘 commit 会数据没写入(close 时回滚)、增删改必须 commit、openSession(true)自动提交不适合业务、Spring 里 @Transactional 管」,就避开了原生使用的高频坑。

六、实践总结:几乎总是用 Spring 事务

总结:实际项目里事务怎么用:

实际项目的事务实践:
  ① 99% 的场景:用 Spring 的 @Transactional
     - MyBatis 整合 Spring 后,事务交给 Spring
     - 你不碰 MyBatis 的事务 API,只标 @Transactional
     - Spring 统一管理(还支持传播行为、隔离级别、回滚规则)

  ② 什么时候涉及 MyBatis 自己的事务:
     - 独立使用 MyBatis(不用 Spring,少见)
     - 理解底层原理(面试、排查问题)

@Transactional 的能力(远超 MyBatis 自带的):
  - 传播行为(REQUIRED/REQUIRES_NEW 等)
  - 隔离级别
  - 回滚规则(默认 RuntimeException 回滚)
  - 声明式(注解,不用手写 commit/rollback)
  - 和其他事务资源统一(不只 MyBatis)

所以结论:
  实际开发用 @Transactional(Spring)
  MyBatis 的 JDBC/MANAGED 是"底层知识"和"独立使用"时才涉及
  别在 Spring 事务里手动 session.commit()(会破坏 Spring 的事务管理)

实践结论:99% 的场景用 Spring 的 @Transactional——MyBatis 整合 Spring 后事务交给 Spring,你只标 @Transactional(还支持传播行为、隔离级别、回滚规则、声明式,远超 MyBatis 自带)。只有独立使用 MyBatis 或理解底层时才涉及 MyBatis 自己的事务别在 Spring 事务里手动 session.commit()(破坏 Spring 的事务管理)。理解「实际开发 99% 用 @Transactional(功能远超 MyBatis 自带:传播/隔离/回滚规则/声明式)、MyBatis 的 JDBC/MANAGED 是底层知识、别在 Spring 事务里手动 commit」,就掌握了事务的实践结论。

记忆钩子:「MyBatis 事务抽象:TransactionFactory+Transaction(getConnection/commit/rollback);两种内置:①JDBC(MyBatis 自己用 Connection 管事务,autoCommit=false,独立使用默认,一个 SqlSession=一个连接=一个事务)②MANAGED(MyBatis 不管,commit/rollback 空操作,交给外部容器 JTA,现在用得少);Spring 整合用 SpringManagedTransaction(跟随 Spring 事务:用 Spring 绑定的连接、自己不提交回滚、听 @Transactional);★原生坑:openSession()默认手动提交,忘 commit 数据没写入(close 时回滚),增删改必须 commit;实际 99%用 @Transactional(功能远超),别在 Spring 事务里手动 commit」

七、常见误区与追问

  • 误区:MyBatis 没有自己的事务管理。 有——MyBatis 有 TransactionFactory + Transaction 抽象,两种内置类型 JDBC(自己用 Connection 管)和 MANAGED(交给外部容器);只是整合 Spring 后让位给 Spring 的事务管理,所以平时感觉不到。
  • 误区:原生 MyBatis 的 insert 执行了就会写入数据库。 openSession() 默认是手动提交模式——insert 后不 commit,操作只在事务里、没生效,close() 时还会回滚(数据没写入);原生做增删改必须 session.commit(),这是高频坑。
  • 误区:openSession(true) 自动提交适合业务。 不适合——自动提交模式每条 SQL 独立提交,多个操作无法组成一个原子事务(一条成功一条失败无法一起回滚);业务事务要用手动提交(自己 commit)或 Spring 的 @Transactional。
  • 误区:Spring 里也要调 session.commit()。 不要——Spring 整合后事务由 @Transactional/DataSourceTransactionManager 统一管理(SpringManagedTransaction 跟随 Spring 事务);在 Spring 事务里手动 session.commit() 会破坏 Spring 的事务管理、导致混乱。
  • 追问:MyBatis 的 JDBC 和 MANAGED 事务类型有什么区别? JDBC:MyBatis 自己用底层 Connection 管理事务(设 autoCommit=false,commit/rollback 直接调 connection),独立使用时的默认;MANAGED:MyBatis 什么都不做(commit/rollback 空操作),把事务交给外部容器(JEE 应用服务器的 JTA)管理,避免和容器事务冲突。
  • 追问:整合 Spring 后 MyBatis 的事务是怎么工作的? 用 SpringManagedTransaction——它从 Spring 的事务同步管理器拿「当前 Spring 事务绑定的连接」执行 SQL,自己不主动提交/回滚,事务的提交/回滚由 Spring 的事务管理器在 @Transactional 结束时统一控制;MyBatis 的操作因此天然纳入 Spring 事务。
  • 追问:为什么实际项目几乎都用 @Transactional 而不用 MyBatis 自己的事务? 因为 @Transactional 功能强大得多——声明式(不用手写 commit/rollback)、支持传播行为(REQUIRED/REQUIRES_NEW)、隔离级别、回滚规则,还能统一管理多种事务资源;MyBatis 自带的只能手动 commit/rollback 单个 SqlSession,能力有限,只在独立使用或理解底层时才用。

八、加强记忆

MyBatis 自己有事务管理,由 TransactionFactory + TransactiongetConnection/commit/rollback/close)抽象,两种内置类型:JDBC——MyBatis 自己用 Connection 管事务autoCommit=falsecommit/rollback 直接调 connection),是独立使用时的默认(一个 SqlSession = 一个 Transaction = 一个 Connection = 一个事务);MANAGED——MyBatis 不管事务commit/rollback 空操作),交给外部容器(JTA)管理,现在用得少。原生使用通过 SqlSession 手动控制事务:openSession() 默认手动提交,忘 commit() 会「数据没写入」(未提交操作在 close 时回滚),增删改必须 commitopenSession(true) 自动提交但不适合业务。和 Spring 整合后,MyBatis 事务让位给 Spring——用 SpringManagedTransaction(跟随 Spring 事务:用 Spring 事务绑定的连接、自己不提交回滚、全听 @Transactional),MyBatis 操作天然纳入 Spring 事务。实际项目 99% 用 Spring 的 @Transactional(功能远超 MyBatis 自带:传播行为、隔离级别、回滚规则、声明式),MyBatis 的 JDBC/MANAGED 只在独立使用或理解底层时涉及;别在 Spring 事务里手动 session.commit()(破坏 Spring 事务管理)。一句话「MyBatis 事务:JDBC(自己用 Connection 管,独立使用默认)/MANAGED(不管交给容器);原生 openSession()默认手动提交、忘 commit 数据没写入;整合 Spring 用 SpringManagedTransaction 跟随 @Transactional;实际 99%用 @Transactional,别在 Spring 事务里手动 commit」。