Savepoint是事务内的临时回滚锚点,通过Connection.setSavepoint()创建,支持命名或匿名;调用Connection.rollback(savepoint)可回滚至该点,保留此前操作,不开启新事务,也不影响外层事务边界。

Java 中的嵌套事务本身并不被 JDBC 或 Spring 默认支持,所谓“嵌套事务”实际是通过 Savepoint(保存点) 实现的局部回滚能力,它不是真正意义上的事务嵌套,而是在一个事务内设置标记点,允许部分回滚到该点,保留之前的操作。
Savepoint 是什么?怎么创建?
Savepoint 是事务内部的一个临时标记,由数据库支持(如 MySQL、PostgreSQL、Oracle),JDBC 提供了 API 创建和回滚到它。它不开启新事务,也不影响外层事务边界。
- 调用
Connection.setSavepoint()创建,返回Savepoint对象 - 必须在事务已开启(即已执行
conn.setAutoCommit(false))后才能设置 - 一个事务中可设多个 Savepoint,按顺序命名或使用匿名方式
如何回滚到指定 Savepoint?
使用 Connection.rollback(savepoint) 可回滚自该保存点之后的所有变更,但不会影响保存点之前的操作,也不会结束当前事务。
- 回滚后,该 Savepoint 及其之后创建的 Savepoint 自动失效
- 仍可继续执行 SQL、提交或回滚整个事务
- 注意:某些数据库(如 MySQL 的 InnoDB)要求 Savepoint 名称唯一,重复名会报错
Spring 中怎么用 Savepoint?
Spring 本身不直接暴露 Savepoint API,但可通过 TransactionStatus 获取底层 Connection,或使用 JdbcTemplate 的回调机制操作:
立即学习“Java免费学习笔记(深入)”;
- 在
TransactionTemplate或声明式事务中,手动获取Connection设置 Savepoint - 推荐封装工具方法,例如:
savepoint = con.setSavepoint("sp1"),出错时con.rollback(savepoint) - Spring 的
Propagation.NESTED在 JDBC 层实际就是靠 Savepoint 实现的(仅对支持的数据库生效)
常见陷阱与注意事项
Savepoint 看似灵活,但容易误用:
- 不是所有数据库都支持 Savepoint(如 H2 默认支持,SQLite 部分版本不支持)
- Savepoint 不解决并发问题,也不能替代事务隔离级别设计
- 频繁创建/回滚 Savepoint 可能影响性能,尤其在大事务中
- 连接池中需确保 Connection 复用时没有残留 Savepoint,否则可能引发异常
不复杂但容易忽略细节。用好 Savepoint 的关键是理解它只是“事务内的撤回锚点”,而非真正的嵌套事务模型。


















