Spring整合mybatis中的sqlSession是如何做到线程隔离的?

本文探讨了Spring与Mybatis整合时,如何在单例bean环境下保证SqlSession的线程安全。通过分析SqlSessionTemplate、SqlSessionInterceptor以及TransactionSynchronizationManager的使用,揭示了SqlSession的线程隔离机制。SqlSessionHolder利用ThreadLocal确保每个线程有自己的SqlSession,避免了线程间的资源冲突。同时,连接池如Druid在数据库连接管理上起到了关键作用,实现了连接复用和线程安全。
摘要由CSDN通过智能技术生成

项目中常常使用mybatis配合spring进行数据库操作,但是我们知道,数据的操作是要求做到线程安全的,而且按照原来的jdbc的使用方式,每次操作完成之后都要将连接关闭,但是实际使用中我们并没有这么干。

更让人疑惑的点是,spring中默认使用单例形式来加载bean,而往往我们也不会改变这种默认,所以,是所有线程共享数据连接?

让我们来看看真相!

自然是要个栗子的:

我们来看下spring中配置mybatis数据库操作bean(使用 druid 连接池):

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:mybatis-config.xml" />
    </bean>

    <!-- scope="prototype" 另说,另讨论,我们先以mapper形式看一下 -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>

    <!-- 事务 -->
    <bean name="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

而在java代码中使用则是使用依赖注入直接使用 @resource sqlSession, 如下:

@Resource
    private SqlSessionTemplate sqlSession;

    @Override
    public User getUser(Map<String, String> cond) {
        // 此句执行db查询
        User result = sqlSession.selectOne(NAME_SPACE
                + ".getUser", cond);
        return result;
    }

这个sqlSession就是直接去操作数据库了看起来是这样,是在bean初始化的时候依赖注入的!

所以,难道每次进入该操作的时候,sqlSession 的实例都会变化吗?答案是否定的。

那么,肯定就是往下使用的时候才发生的变化呗!

再往下走,可以看到,调用了一个代理来进行具体的查询!

  // org/mybatis/spring/SqlSessionTemplate.selectOne()
  public <T> T selectOne(String statement, Object parameter) {
    return this.sqlSessionProxy.<T> selectOne(statement, parameter);
  }

为啥要用代理呢?自己直接查不就行了吗?其实,用代理是有好处的,那就可以可以进行另外的包装!

代理是怎么生成的呢?其实只要看一下 SqlSessionTemplate 的构造方法就知道了!

/**
   * Constructs a Spring managed {@code SqlSession} with the given
   * {@code SqlSessionFactory} and {@code ExecutorType}.
   * A custom {@code SQLExceptionTranslator} can be provided as an
   * argument so any {@code PersistenceException} thrown by MyBatis
   * can be custom translated to a {@code RuntimeException}
   * The {@code SQLExceptionTranslator} can also be null and thus no
   * exception translation will be done and MyBatis exceptions will be
   * thrown
   *
   * @param sqlSessionFactory
   * @param executorType
   * @param exceptionTranslator
   */
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    // 生成代理 SqlSessionInterceptor 为 InvocationHandler
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSe
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值