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 会自动处理这些细节。

相关推荐
bing_1583 小时前
跨多个微服务使用 Redis 共享数据时,如何管理数据一致性?
redis·微服务·mybatis
风铃儿~6 小时前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
云之兕6 小时前
MyBatis 的动态 SQL
数据库·sql·mybatis
hstar95279 小时前
三十三、面向对象底层逻辑-SpringMVC九大组件之HandlerExceptionResolver接口设计
java·spring·设计模式·架构
面朝大海,春不暖,花不开9 小时前
Spring Security默认配置覆盖指南
java·后端·spring
IT_Octopus11 小时前
多线程下使用缓存+锁Lock, 出现“锁失效” + “缓存未命中竞争”的缓存击穿情况,双重检查缓存解决问题
java·spring·缓存
loser.loser14 小时前
QQ邮箱发送验证码(Springboot)
java·spring boot·mybatis
qq_3380329216 小时前
Spring Boot/Spring应用中配置自定义RedisTemplate
spring boot·redis·spring
毅航16 小时前
Trae复刻Mybatis之旅(一):创建SqlSession会话,构建代理
后端·mybatis·trae
考虑考虑17 小时前
Springboot3.5.x版本actuator新属性
spring boot·后端·spring