MyBatis会话模块详解

深入剖析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各有千秋
相关推荐
hssfscv37 分钟前
Javaweb学习笔记——JDBC和Mybatis
笔记·学习·mybatis
cike_y1 小时前
Spring整合Mybatis:dao层
java·开发语言·数据库·spring·mybatis
雨中飘荡的记忆1 小时前
MyBatis设计模式之构建者、工厂、代理模式
mybatis
while(1){yan}11 小时前
Mybatis基础(详解)
spring boot·spring·java-ee·mybatis
多多*14 小时前
2026年1月3日八股记录
java·开发语言·windows·tcp/ip·mybatis
草原印象17 小时前
Spring、SpringMVC、Mybatis框架整合实战视频课程
java·spring·mybatis
雨中飘荡的记忆19 小时前
MyBatis映射器模块详解
mybatis
计算机学姐21 小时前
基于SpringBoot的个人健康管理系统【2026最新】
java·spring boot·后端·mysql·spring·intellij-idea·mybatis
气π1 天前
【JavaWeb】——帝可得实践项目-App与设备端补充
java·spring boot·mybatis