Mybatis 源码解析(二) DefaultSqlSession 为什么不是线程安全的

DefaultSqlSession 的执行依赖于 BaseExecutor,其中的 localCache 使用 HashMap 缓存查询结果,引发线程安全问题。当不同线程并发查询时,可能导致缓存数据不一致。例如,线程1查询5条数据后,线程2查询时可能获取到新插入的数据,进而导致错误的缓存结果。解决办法是创建线程绑定的 SqlSession 实现,确保每次执行时使用独立的会话,避免共享竞争资源。
摘要由CSDN通过智能技术生成

DefaultSqlSession 真正的执行代码是由 BaseExecutor 进行执行的,而BaseExecutor中的代码如下:

定义一个localCache字段,其实底层就是一个HashMap。

  protected PerpetualCache localCache;
public class PerpetualCache implements Cache {
   

  private final String id;

  private Map<Object, Object> cache = new HashMap<Object, Object>();

  public PerpetualCache(String id) {
   
    this.id = id;
  }
  
  @Override
  public void putObject(Object key, Object value) {
   
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
   
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
   
    return cache.remove(key);
  }
  // .....

BaseExecutor 用这个HashMap缓存了 SqlSession的 select/selectList的查询结果。

所以这个可以理解成一个共享竞争资源

我们看一下BaseExecutor的查询数据库代码

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
   
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
   
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
   
      localCache.removeObject(key);
    }
    // ①
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
   
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

当不同的线程使用sqlsession进行查询的时候,会出现以下的情况

  1. 线程1 使用select * from user 进行查询,查询出5条数据,
  2. 线程2同样使用语句,并且这个时候是线程1刚好执行到①处,则还没有放入缓存,这个时候直接查库,但是线程2查询的时候,数据库刚好新插入了1条,所以查询到的是6条,
  3. 线程2把查询到的6条放入缓存
  4. 然后线程1又把第5条放入了缓存,
  5. 这样就导致了后续的查询都使用的是线程1的缓存,导致查询结果不正确。

在这里插入图片描述

附一个线程安全的SqlSession

实现思路就是创建一个包装类,每一次真正执行的时候,使用的是当前绑定线程的SqlSession

package com.wuhulala.springboot.mybatis.threadsafe;

import org.apache.ibatis.cursor.Cursor
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值