
无论事务中仅执行单条查询、单条更新,还是空操作,只要调用 begintransaction() 启动了事务,就必须在结束时明确调用 commit() 或 rollback();否则将导致连接池资源占用、锁未释放、事务状态异常等严重问题。
无论事务中仅执行单条查询、单条更新,还是空操作,只要调用 begintransaction() 启动了事务,就必须在结束时明确调用 commit() 或 rollback();否则将导致连接池资源占用、锁未释放、事务状态异常等严重问题。
在 Hibernate(以及绝大多数 JDBC 兼容的 ORM 框架)中,beginTransaction() 并非“声明式标记”,而是一个真实开启数据库事务的操作——它会向底层数据库连接发送 BEGIN(或等效)指令,并绑定当前 Session 与一个活跃事务上下文。这意味着:
- 即使你只执行 session.createQuery("from User").list()(只读查询),该查询仍运行在已开启的事务中;
- 即使你只执行 session.delete(...) 一条 DML 语句,事务也已处于“进行中”(ACTIVE)状态;
- 更关键的是:若不显式结束事务,数据库连接不会自动关闭事务,连接可能被归还至连接池,但其事务状态仍为“未决”(in-doubt)。
❗ 忽略 rollback() 的后果示例
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction(); // ← 事务已开启
List<User> users = sess.createQuery("from User", User.class).list(); // 只读,但仍在事务内
// 假设此处抛出 NullPointerException(如 users 为 null 后续处理出错)
processUsers(users);
tx.commit(); // ← 永远不会执行
} catch (Exception e) {
// ❌ 错误:未调用 tx.rollback()
throw e; // 异常抛出,tx 仍处于 ACTIVE 状态!
} finally {
sess.close(); // 连接可能被归还给连接池,但事务未终结
}此时可能出现:
- 数据库侧:行级锁未释放(尤其在可重复读隔离级别下)、undo log 持续增长;
- 应用侧:连接池耗尽(因连接被“假占用”)、后续获取连接时触发超时或死锁;
- 分布式场景:XA 事务挂起,影响全局一致性。
✅ 正确模式:始终配对 commit/rollback
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// 任意操作:查询、插入、更新、删除、甚至无操作
List<User> users = sess.createQuery("from User", User.class).list();
// session.persist(new User("Alice"));
// session.delete(session.get(User.class, 123));
tx.commit(); // 成功则提交
} catch (Exception e) {
if (tx != null && tx.getStatus().canRollback()) {
try {
tx.rollback(); // 必须回滚 —— 清理事务上下文
} catch (RuntimeException rbEx) {
// 记录 rollback 失败(罕见但需关注)
log.warn("Rollback failed after exception", rbEx);
}
}
throw e; // 保持原始异常语义
} finally {
sess.close();
}⚠️ 注意事项
- 不要依赖“单条语句自动回滚”:数据库不会因 Java 异常自动终止事务;JDBC 驱动和 ORM 不会为你兜底。
- 检查事务状态再 rollback:tx.getStatus().canRollback() 可避免对已提交/已标记回滚的事务重复操作(Hibernate 5.2+ 推荐)。
- 使用 try-with-resources?不适用:Session 不是 AutoCloseable 的事务管理者,close() 不等于 rollback()。
- 现代替代方案:优先考虑 Spring 的 @Transactional(由 AOP 自动管理 commit/rollback),但理解底层逻辑仍是排查事务泄漏的关键。
总之,rollback() 不是对“多条更新”的保险丝,而是对“已开启事务”的强制收尾契约——这是数据库连接正确复用与系统稳定运行的底线要求。

















