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 并调用其方法(如 selectOneinsert 等)时:
      • 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 自动代理(通过 MapperFactoryBeanMapperScannerConfigurer)。
  2. UserService 中的 getUseraddUser 方法使用了 @Transactional 注解,表示这些方法需要在事务中执行。
  3. 当调用 getUseraddUser 方法时:
    • 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 会自动处理这些细节。

相关推荐
drebander1 小时前
MyBatis-Plus 逻辑删除实现
tomcat·mybatis·mybatis-plus
源码code1 小时前
基于微信小程序的停车场管理系统的设计与实现
java·python·spring·tomcat
小钊(求职中)2 小时前
最新Git入门到精通完整教程
java·git·后端·spring·面试
WeiLai11124 小时前
面试基础---Spring Cloud微服务负载均衡架构
spring boot·分布式·后端·spring·spring cloud·面试·架构
IT闫6 小时前
【SpringSecurity】——认证、注销、权限控制和注销、记住密码、自定义登入页等知识总结
spring·安全架构
m0_748238636 小时前
【SpringBoot3】Spring Boot 3.0 集成 Mybatis Plus
spring boot·后端·mybatis
weixin_419658317 小时前
SpringMVC中的常用注解和用法
java·spring·java-ee
奔跑吧邓邓子8 小时前
Spring AI:开启Java开发的智能新时代
java·人工智能·spring
gentle coder13 小时前
【框架】Spring、SpringBoot和SpringCloud区别
spring boot·spring·spring cloud
没有十八岁16 小时前
云创智城YunCharge 新能源二轮、四轮充电解决方案(云快充、万马爱充、中电联、OCPP1.6J等多个私有单车、汽车充电协议)之新能源充电行业系统说明书
java·数据库·spring·汽车