深入剖析MyBatis核心------SqlSession的前世今生
一、MyBatis整体架构与会话模块
MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的工作。
1.1 MyBatis整体架构
MyBatis采用分层架构设计,从上到下分为:
sql
• 应用层 --- 与用户应用程序直接交互
• 接口层 --- 提供SqlSession和Mapper接口
• 核心处理层 --- 包括SQL解析、执行、结果映射
• 基础支撑层 --- 包括反射、类型处理、日志、缓存、事务
• 数据层 --- 数据源管理、连接池、JDBC驱动
会话模块位于接口层,是MyBatis与应用程序交互的主要入口。

1.2 会话模块的核心职责
会话模块在MyBatis中承担以下核心职责:
sql
✅ 数据库操作接口 --- 提供insert、update、delete、select等方法
✅ SqlSession生命周期管理 --- 创建、使用、关闭SqlSession
✅ 事务控制 --- 管理事务的开启、提交、回滚
✅ Mapper管理 --- 获取Mapper接口的代理对象
✅ 批量操作支持 --- 提供批量执行SQL的能力
二、SqlSession接口详解
SqlSession是MyBatis的核心接口之一,它定义了所有数据库操作的方法。
2.1 SqlSession核心方法分类
scss
// 查询单个对象
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
// 查询列表
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
// 查询Map
<K, V> Map<K, V> selectMap(String statement, String mapKey);
// 游标查询
<T> Cursor<T> selectCursor(String statement);
// 插入
int insert(String statement);
int insert(String statement, Object parameter);
// 更新
int update(String statement);
int update(String statement, Object parameter);
// 删除
int delete(String statement);
int delete(String statement, Object parameter);
// 提交事务
void commit();
void commit(boolean force);
// 回滚事务
void rollback();
void rollback(boolean force);
// 获取Mapper代理对象
<T> T getMapper(Class<T> type);
// 1. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
// 2. 打开SqlSession
try (SqlSession session = factory.openSession()) {
// 3. 执行SQL
User user = session.selectOne(
"com.example.mapper.UserMapper.selectById", 1);
System.out.println(user);
}
try (SqlSession session = factory.openSession()) {
// 获取Mapper代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
// 调用Mapper方法
User user = mapper.selectById(1);
System.out.println(user);
session.commit();
}
三、SqlSession的实现类
MyBatis提供了两个主要的SqlSession实现类。
3.1 DefaultSqlSession
DefaultSqlSession是SqlSession的默认实现类,最常用的实现。
核心代码片段:
typescript
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
@Override
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException(
"Expected one result but found: " + list.size());
}
return null;
}
@Override
public void commit(boolean force) {
try {
executor.commit(isCommitOrRollbackRequired(force));
dirty = false;
} catch (Exception e) {
throw ExceptionFactory.wrapException(
"Error committing transaction.", e);
}
}
}
3.2 SqlSessionManager
SqlSessionManager同时实现了SqlSession和SqlSessionFactory接口,既可以作为SqlSession使用,也可以作为工厂使用。
csharp
public class SqlSessionManager
implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
private ThreadLocal<SqlSession> localSqlSession
= new ThreadLocal<>();
// 开启本地Session
public void startManagedSession() {
this.localSqlSession.set(openSession());
}
// 关闭本地Session
public void closeManagedSession() {
SqlSession sqlSession = localSqlSession.get();
if (sqlSession != null) {
try {
sqlSession.close();
} finally {
localSqlSession.remove();
}
}
}
}

四、SqlSessionFactory工厂
SqlSessionFactory是创建SqlSession的工厂接口。
4.1 SqlSessionFactory接口方法
scss
public interface SqlSessionFactory {
// 常用方法:打开Session
SqlSession openSession();
// 指定ExecutorType
SqlSession openSession(ExecutorType execType);
// 指定是否自动提交
SqlSession openSession(boolean autoCommit);
// 指定事务隔离级别
SqlSession openSession(TransactionIsolationLevel level);
// 使用指定连接
SqlSession openSession(Connection connection);
}
4.2 DefaultSqlSessionFactory核心流程
创建SqlSession的完整流程:
java
private SqlSession openSessionFromDataSource(
ExecutorType execType,
TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
//获取环境配置
Environment environment = configuration.getEnvironment();
//创建事务工厂
TransactionFactory transactionFactory =
getTransactionFactoryFromEnvironment(environment);
//创建事务
tx = transactionFactory.newTransaction(
environment.getDataSource(), level, autoCommit);
//创建执行器
Executor executor = configuration.newExecutor(tx, execType);
//创建DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException(
"Error opening session.", e);
}
}

五、SqlSession生命周期管理
SqlSession的生命周期管理非常重要,不当使用会导致资源泄漏。
5.1 SqlSession的三个阶段
ini
// 方式1:默认创建
SqlSession session = sqlSessionFactory.openSession();
// 方式2:自动提交
SqlSession session = sqlSessionFactory.openSession(true);
// 方式3:指定Executor类型
SqlSession session = sqlSessionFactory.openSession(
ExecutorType.BATCH);
// 方式4:事务隔离级别
SqlSession session = sqlSessionFactory.openSession(
TransactionIsolationLevel.READ_COMMITTED);
try (SqlSession session = factory.openSession()) {
// 执行查询
User user = session.selectOne(
"com.example.mapper.UserMapper.selectById", 1);
// 执行更新
User updateUser = new User();
updateUser.setId(1);
updateUser.setName("张三");
session.update(
"com.example.mapper.UserMapper.update", updateUser);
// 提交事务
session.commit();
}
// 推荐方式:try-with-resources
try (SqlSession session = factory.openSession()) {
// 使用session
} // 自动关闭
// 传统方式
SqlSession session = null;
try {
session = factory.openSession();
// 使用session
} finally {
if (session != null) {
session.close();
}
}
重要原则:SqlSession的作用域应该是方法级别的!
java
// 错误:SqlSession作为成员变量
public class UserService {
private SqlSession session; // ❌ 不要这样做!
public User getUserById(int id) {
return session.selectOne(
"com.example.mapper.UserMapper.selectById", id);
}
}
// 正确:SqlSession在方法中创建和关闭
public class UserService {
private SqlSessionFactory factory;
public User getUserById(int id) {
try (SqlSession session = factory.openSession()) {
return session.selectOne(
"com.example.mapper.UserMapper.selectById", id);
}
}
}
5.3 线程安全问题
SqlSession不是线程安全的,每个线程都应该有自己的SqlSession实例!
java
public class UserService {
// ❌ 多个线程共享同一个SqlSession
private SqlSession session = factory.openSession();
}
public class UserService {
private SqlSessionFactory factory;
public User getUserById(int id) {
// ✅ 每次调用都创建新的SqlSession
try (SqlSession session = factory.openSession()) {
return session.selectOne("...", id);
}
}
}
5.4 ThreadLocal管理模式
在某些场景下,可以使用ThreadLocal管理SqlSession:
csharp
public class SqlSessionManager {
private static final ThreadLocal<SqlSession> LOCAL_SESSION
= new ThreadLocal<>();
private static SqlSessionFactory factory;
// 获取当前线程的SqlSession
public static SqlSession getSession() {
SqlSession session = LOCAL_SESSION.get();
if (session == null) {
session = factory.openSession();
LOCAL_SESSION.set(session);
}
return session;
}
// 关闭当前线程的SqlSession
public static void closeSession() {
SqlSession session = LOCAL_SESSION.get();
if (session != null) {
session.close();
LOCAL_SESSION.remove();
}
}
}

六、事务控制机制
SqlSession提供了完善的事务控制机制。
6.1 自动提交 vs 手动提交
ini
// 不自动提交(默认)
SqlSession session = factory.openSession();
session.insert("...");
session.commit(); // 需要手动提交
//自动提交
SqlSession session = factory.openSession(true);
session.insert("..."); // 自动提交
6.2 手动事务控制实战
scss
try (SqlSession session = factory.openSession()) {
try {
// 1.插入用户
User user = new User();
user.setName("张三");
user.setEmail("zhangsan@example.com");
session.insert(
"com.example.mapper.UserMapper.insert", user);
//2.插入订单
Order order = new Order();
order.setUserId(user.getId());
order.setAmount(100.0);
session.insert(
"com.example.mapper.OrderMapper.insert", order);
//3.提交事务
session.commit();
} catch (Exception e) {
//4.回滚事务
session.rollback();
throw e;
}
}
6.3 事务隔离级别
MyBatis支持设置事务隔离级别:
ini
SqlSession session = factory.openSession(
TransactionIsolationLevel.READ_COMMITTED);
支持的隔离级别:
| 隔离级别 | 说明 | 适用场景 |
|---|---|---|
| NONE | 无事务隔离 | 不推荐 |
| READ_UNCOMMITTED | 读未提交 | 极少使用 |
| READ_COMMITTED | 读已提交 | 大多数数据库默认 |
| REPEATABLE_READ | 可重复读 | MySQL默认 |
| SERIALIZABLE | 串行化 | 最高隔离级别 |
6.4 批量操作的事务控制
ini
try (SqlSession session = factory.openSession(
ExecutorType.BATCH, false)) {
try {
UserMapper mapper = session.getMapper(UserMapper.class);
// 批量插入
for (int i = 0; i < 1000; i++) {
User user = new User();
user.setName("User" + i);
user.setEmail("user" + i + "@example.com");
mapper.insert(user);
}
// 统一提交
session.commit();
} catch (Exception e) {
session.rollback();
throw e;
}
}

七、SqlSession与Mapper集成
SqlSession与Mapper接口的集成是MyBatis最常用的方式。
7.1 定义Mapper接口
less
public interface UserMapper {
User selectById(@Param("id") Integer id);
List<User> selectAll();
int insert(User user);
int update(User user);
int deleteById(@Param("id") Integer id);
}
7.2 Mapper XML映射文件
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<resultMap id="BaseResultMap"
type="com.example.entity.User">
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="name" property="name"
jdbcType="VARCHAR"/>
<result column="email" property="email"
jdbcType="VARCHAR"/>
</resultMap>
<select id="selectById" resultMap="BaseResultMap">
SELECT id, name, email, age
FROM t_user
WHERE id = #{id}
</select>
<insert id="insert" parameterType="com.example.entity.User"
useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_user (name, email, age)
VALUES (#{name}, #{email}, #{age})
</insert>
</mapper>
7.3 通过SqlSession获取Mapper
ini
try (SqlSession session = factory.openSession()) {
// 获取Mapper代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
//调用Mapper方法
User user = mapper.selectById(1);
//插入数据
User newUser = new User();
newUser.setName("李四");
newUser.setEmail("lisi@example.com");
mapper.insert(newUser);
//提交事务
session.commit();
}
7.4 MapperProxy工作原理
MyBatis通过JDK动态代理为Mapper接口生成代理对象:
kotlin
public class MapperProxy<T> implements InvocationHandler {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// 如果是Object类的方法,直接执行
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// 获取Mapper方法
final MapperMethod mapperMethod =
cachedMapperMethod(method);
// 执行Mapper方法
return mapperMethod.execute(sqlSession, args);
}
}

八、Executor执行器详解
Executor是SqlSession的核心组件,负责实际执行SQL语句。
8.1 三种Executor类型
ini
// 默认的Executor,每次执行都会创建新的Statement
SqlSession session = factory.openSession(ExecutorType.SIMPLE);
特点:简单直接,每次创建新Statement
适用场景:一般查询、单条操作
ini
// 重用Statement,减少创建开销
SqlSession session = factory.openSession(ExecutorType.REUSE);
特点:重用Statement,减少创建开销
适用场景:执行相同SQL多次
ini
// 批量执行SQL,性能最高
SqlSession session = factory.openSession(ExecutorType.BATCH);
特点:批量执行,性能最优
适用场景:批量插入、更新
8.2 Executor对比表
| Executor类型 | 性能 | Statement复用 | 适用场景 |
|---|---|---|---|
| SIMPLE | ⭐⭐⭐ | ❌ | 一般查询 |
| REUSE | ⭐⭐⭐⭐ | ✅ | 相同SQL多次执行 |
| BATCH | ⭐⭐⭐⭐⭐ | ✅ | 批量操作 |
8.3 实战示例
ini
//BATCH执行器批量插入示例
try (SqlSession session = factory.openSession(
ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
for (int i = 0; i < 1000; i++) {
User user = new User();
user.setName("User" + i);
mapper.insert(user);
}
// 批量提交
session.commit();
}
九、最佳实践
9.1 SqlSession使用黄金法则
csharp
✅ 及时关闭 --- 使用try-with-resources确保资源释放
✅ 方法作用域 --- SqlSession应该在方法级别创建和关闭
✅ 线程独立 --- 每个线程使用独立的SqlSession
✅ 事务控制 --- 明确提交或回滚事务
✅ 异常处理 --- 捕获异常并回滚事务
9.2 Spring集成最佳实践
在Spring中使用MyBatis时,SqlSession由容器管理:
typescript
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void createUser(User user) {
// SqlSession由Spring管理,无需手动关闭
userMapper.insert(user);
}
}
9.3 常见问题排查
java
// 错误示例
SqlSession session = factory.openSession();
User user = session.selectOne("...");
//忘记关闭 → 资源泄漏
//正确示例
try (SqlSession session = factory.openSession()) {
User user = session.selectOne("...");
} // 自动关闭
// 错误示例
try (SqlSession session = factory.openSession()) {
session.insert("...");
// 忘记提交 → 数据未保存
}
//正确示例
try (SqlSession session = factory.openSession()) {
session.insert("...");
session.commit(); // 记得提交
}
//错误示例
private SqlSession session = factory.openSession();
//正确示例
public User getUserById(Integer id) {
try (SqlSession session = factory.openSession()) {
return session.selectOne("...", id);
}
}
十、总结
通过本文的学习,我们深入了解了MyBatis会话模块的核心内容:
SqlSession接口 --- 定义了所有数据库操作方法
SqlSessionFactory --- 负责创建和管理SqlSession实例
生命周期管理 --- 方法级别创建,线程独立使用
事务控制 --- 支持手动提交和自动提交两种模式
Mapper集成 --- 通过动态代理实现类型安全操作
Executor类型 --- SIMPLE、REUSE、BATCH各有千秋