Spring中是如何解决MyBatis的SqlSession的线程安全问题的?

MyBatis 的 SqlSession 本身不是线程安全的。每个线程都拥有自己的 SqlSession 实例。Spring 通过与 MyBatis 的集成,以及 Spring 自身的事务管理机制,解决了 SqlSession 的线程安全问题。主要有两种方式:

1. SqlSessionFactoryBean + SqlSessionTemplate (传统方式):

  • SqlSessionFactoryBean:

    • Spring 提供的 FactoryBean,用于创建 MyBatis 的 SqlSessionFactory。
    • SqlSessionFactoryBean 本身是单例的,并且是线程安全的。
    • 它在 Spring 容器启动时创建 SqlSessionFactory 实例。
  • SqlSessionTemplate:

    • 实现了 SqlSession 接口,是 SqlSession 的线程安全代理。
    • SqlSessionTemplate 内部使用 ThreadLocal 来保存每个线程的 SqlSession。
    • 当需要 SqlSession 时,SqlSessionTemplate 会从 ThreadLocal 中获取当前线程的 SqlSession,如果没有,则创建一个新的 SqlSession 并绑定到当前线程。
    • 在事务环境下,SqlSessionTemplate 会参与 Spring 的事务管理。
  • 工作原理:

    1. Spring 容器启动时,SqlSessionFactoryBean 创建 SqlSessionFactory。
    2. 当你注入 SqlSessionTemplate 并调用其方法(如 selectOne、insert 等)时:
      • SqlSessionTemplate 检查当前线程是否已经绑定了 SqlSession。
      • 如果没有,SqlSessionTemplate 会从 SqlSessionFactory 获取一个新的 SqlSession,并将其绑定到当前线程的 ThreadLocal。
      • 如果已经绑定,则直接使用 ThreadLocal 中的 SqlSession。
      • SqlSessionTemplate 将方法调用委托给实际的 SqlSession 执行。
      • 如果当前存在 Spring 管理的事务,SqlSessionTemplate 会参与事务(获取事务的 SqlSession,并在事务提交或回滚时关闭 SqlSession)。
      • 如果没有事务,SqlSessionTemplate 会在使用完 SqlSession 后自动关闭它 (通常是在方法调用结束后)。

2. MapperFactoryBean / MapperScannerConfigurer (更常用, 特别是与 Spring Boot 结合):

  • MapperFactoryBean:

    • Spring 提供的 FactoryBean,用于创建 MyBatis 的 Mapper 接口的代理对象。
    • MapperFactoryBean 内部使用 SqlSessionTemplate 来执行 SQL 操作。
    • 你可以直接注入 Mapper 接口,而无需显式使用 SqlSessionTemplate。
  • MapperScannerConfigurer:

    • Spring 提供的 BeanDefinitionRegistryPostProcessor,用于自动扫描 Mapper 接口,并为每个 Mapper 接口创建 MapperFactoryBean。
    • 使用 @MapperScan 注解(或 XML 配置)可以启用 MapperScannerConfigurer。
    • Spring Boot 会自动配置 MapperScannerConfigurer(如果你使用了 mybatis-spring-boot-starter)。
  • 工作原理:

    1. Spring容器启动时, MapperScannerConfigurer (或手动配置的MapperFactoryBean) 会扫描并注册Mapper接口。
    2. 对于每个Mapper接口, 会创建一个MapperFactoryBean.
    3. 当你注入 Mapper 接口并调用其方法时:
      • Spring 会调用 MapperFactoryBean 创建 Mapper 接口的代理对象(如果尚未创建)。
      • 代理对象内部会使用 SqlSessionTemplate 来获取当前线程的 SqlSession(如果需要,会创建新的 SqlSession 并绑定到当前线程)。
      • 代理对象将方法调用委托给 SqlSession 执行相应的 SQL 操作。
      • 事务的处理与SqlSessionTemplate相同.

Spring 的事务管理:

  • Spring 的事务管理机制(声明式事务 @Transactional 或编程式事务)与 MyBatis 的集成是解决 SqlSession 线程安全问题的关键。
  • 当使用 @Transactional 注解时,Spring 会为当前线程创建一个事务上下文,并将 SqlSession 绑定到该事务上下文。
  • 在同一个事务中,所有对 SqlSession 的操作都会使用同一个 SqlSession 实例,保证了事务的一致性。
  • 事务提交或回滚时,Spring 会自动关闭 SqlSession。

代码示例 (使用 MapperFactoryBean 和 @Transactional):

UserMapper.java:

java 复制代码
public interface UserMapper {
    User getUserById(int id);
    void insertUser(User user);
}

UserService.java

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Transactional
    public User getUser(int id) {
        return userMapper.getUserById(id);
    }

      @Transactional
    public void addUser(User user){
        userMapper.insertUser(user);
        // ... 其他操作 ...
    }
}

配置 (Spring Boot):

java 复制代码
// 假设你已经添加了 mybatis-spring-boot-starter 依赖
// application.properties
mybatis.mapper-locations=classpath:mapper/*.xml
# 其他配置...

在这个例子中:

  1. UserMapper 接口会被 Spring 自动代理(通过 MapperFactoryBean 或 MapperScannerConfigurer)。
  2. UserService 中的 getUser 和 addUser 方法使用了 @Transactional 注解,表示这些方法需要在事务中执行。
  3. 当调用 getUser 或 addUser 方法时:
    • Spring 会开启一个事务。
    • Spring 会获取或创建一个 SqlSession,并将其绑定到当前线程的事务上下文。
    • UserMapper 的代理对象会使用该 SqlSession 执行 SQL 操作。
    • 如果方法成功执行,Spring 会提交事务并关闭 SqlSession。
    • 如果方法抛出异常,Spring 会回滚事务并关闭 SqlSession。

总结:

Spring 通过以下方式解决 MyBatis 的 SqlSession 线程安全问题:

  • SqlSessionTemplate: 使用 ThreadLocal 为每个线程提供独立的 SqlSession 实例。
  • MapperFactoryBean / MapperScannerConfigurer: 自动创建 Mapper 接口的代理对象,代理对象内部使用 SqlSessionTemplate。
  • Spring 事务管理: 将 SqlSession 绑定到当前线程的事务上下文,保证同一个事务中的所有操作使用同一个 SqlSession。

通过这些机制,Spring 确保了在多线程环境下 SqlSession 的安全使用,并简化了 MyBatis 与 Spring 的集成。 开发过程中咱们无需手动管理 SqlSession 的创建、获取和关闭,Spring 会自动处理这些细节。

相关推荐
captain3767 小时前
MyBatis(入门5)其他查询操作
java·数据库·spring boot·mybatis
2601_962885728 小时前
quotes 字段全解:涨跌幅、振幅、换手率的口径与单位
java·前端·spring
步行cgn9 小时前
Spring MVC 拦截器 Interceptor 详解:从执行流程到实战应用
java·spring·mvc
开开心心_Every9 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
captain3769 小时前
MyBatis(进阶1)
mybatis
萧瑟余晖10 小时前
Spring Batch核心模型与快速上手详解
java·spring·batch
开开心心就好13 小时前
音频降噪剪辑工具,音量裁剪一键处理
运维·服务器·前端·spring·ocr·音视频·1024程序员节
Flynn Liang14 小时前
大模型怎么自己调函数?Spring AI 2.0 Tool Calling 自动循环原理 + 完整时序图
java·人工智能·spring·agent·函数调用·springai·toolcalling
乌暮15 小时前
请求进门先安检:Spring 拦截器与统一功能实战指南
java·开发语言·后端·学习·spring
CoderYanger19 小时前
Java EE 进阶:5. Spring IoC&DI(第三弹):Spring DI
java·开发语言·spring boot·spring·面试·java-ee·mvc