sqlsessionfactory 应为单例,sqlsession 必须按需创建并及时关闭;前者是重量级对象,整个应用只需一个实例,推荐 spring 管理或静态内部类实现线程安全单例;后者非线程安全、含连接与缓存等上下文,每线程应独享,用后必须 close,不可复用或池化。

SqlSessionFactory 本身就应该设计为单例,但它不负责管理 SqlSession 的生命周期,更不会“单例管理 SqlSession”。这是常见的误解。SqlSession 是非线程安全、一次性的会话对象,必须按需创建、及时关闭,不能复用或全局单例。
SqlSessionFactory 应该是单例(且通常只需一个)
SqlSessionFactory 是重量级对象,内部封装了 MyBatis 的配置、映射信息、数据源等,创建开销大。因此:
- 整个应用生命周期中,一般只初始化一个 SqlSessionFactory 实例;
- 推荐通过 Spring 容器管理(
@Bean+ 单例作用域),或手动使用静态内部类/枚举实现线程安全的懒汉单例; - 不要每次执行 SQL 都 new 一个 SqlSessionFactory —— 这会导致资源浪费和配置重复加载。
SqlSession 必须按需创建、用完即关
SqlSession 包含数据库连接、事务状态、一级缓存等上下文,不是线程安全的:
- 每个线程(如 Web 请求)应获取自己的 SqlSession 实例;
- 典型用法:调用
sqlSessionFactory.openSession()获取,操作完成后必须显式close()(建议用 try-with-resources); - 若交由 Spring 管理,Spring 会在事务边界自动创建和销毁 SqlSession(基于
SqlSessionTemplate或MapperFactoryBean)。
常见错误:试图“复用”或“池化” SqlSession
有人想缓存 SqlSession 提高性能,这是危险的:
- 多个线程共用同一个 SqlSession 可能引发并发修改异常(如
ConcurrentModificationException)或脏读; - 未关闭的 SqlSession 会占用数据库连接,导致连接池耗尽;
- 一级缓存可能污染(不同请求看到彼此的缓存结果);
- 事务状态混乱(比如 commit 后再执行查询会报错)。
正确实践示例(非 Spring 环境)
手动管理时,确保 SqlSession 生命周期清晰:
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectById(1L);
System.out.println(user);
} // 自动 close,释放连接和清理缓存
Spring 环境下,直接注入 Mapper 接口即可,无需接触 SqlSession —— 框架已帮你完成线程绑定、事务管理和自动关闭。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











