sqlsession 不是线程安全的,因其内部的 connection 和一级缓存无同步机制,多线程共用会导致脏读、事务覆盖和参数绑定错乱;应为每个线程分配独立实例并及时释放。

MyBatis 的 SqlSession 本身不是线程安全的,多线程共享会导致事务错乱、一级缓存污染、连接冲突甚至空指针异常。解决的关键不是给它加锁,而是让每个线程拥有自己独立的 SqlSession 实例,并在其使用结束后及时释放。
为什么 SqlSession 不能共用?
SqlSession 内部持有数据库连接(Connection)和一级缓存(基于 HashMap),二者均无同步机制:
- 线程 A 执行了 update 但未 commit,线程 B 用同一个 SqlSession 查询,可能读到未提交的脏数据;
- 多个线程同时调用
commit()或rollback(),事务状态会被相互覆盖; - Mapper 方法参数绑定过程存在竞态,可能导致 SQL 执行与预期不符。
Spring 环境下推荐用 SqlSessionTemplate
这是最省心、最可靠的方案。SqlSessionTemplate 是 Spring 提供的线程安全代理类,底层自动结合 ThreadLocal 和事务管理器实现会话隔离:
- 每次调用 Mapper 方法时,动态获取当前线程绑定的 SqlSession;
- 事务开始时创建或复用 SqlSession,事务结束自动清理缓存、归还连接、解除绑定;
- 开发者无需手动 open/close,也避免了静态变量或单例误持 SqlSession 的风险。
只需确保 Mapper 接口由 Spring 托管(如通过 @MapperScan),所有注入的 Mapper 实际调用的都是 SqlSessionTemplate 封装后的逻辑。
纯 MyBatis 场景用 ThreadLocal 封装工具类
若项目未集成 Spring(例如多线程批处理任务),可自行封装线程级 SqlSession 工具类:
- 声明
static ThreadLocal<sqlsession></sqlsession>,每个线程首次调用时创建新会话; - 业务逻辑必须用
try-with-resources或try-finally显式关闭 SqlSession; - 严禁将 SqlSession 存入静态字段、实例变量、HttpSession 或任何跨线程容器中。
示例关键逻辑:
SqlSession sqlSession = threadLocal.get();<br>
if (sqlSession == null) {<br>
sqlSession = sqlSessionFactory.openSession();<br>
threadLocal.set(sqlSession);<br>
}
记住两个不可动摇的前提
SqlSessionFactory 是线程安全的,整个应用生命周期内只创建一次,可被所有线程共享;
SqlSession 必须按需创建、及时关闭,它的作用域应严格限制在单次数据库操作或一个完整事务内。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











