Mybatis源码分析-一级缓存

本文详细介绍了MyBatis中的一级缓存机制,包括缓存标识的生成过程、缓存数据的获取与更新流程,并探讨了一级缓存在实际应用中的作用与限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了能提高系统性能,缓存必不可少,mybatis同样也是有缓存的!
分一级缓存和二级缓存,该篇介绍下一级缓存!!
我们回顾下配置阶段,再创建executor执行器时,有个Configuration.newExecutor方法,来看下

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //默认cacheEnabled为true,那么此时的执行器其实是CachingExecutor,将executor当参数传递给CachingExecutor,装饰了下,增加了缓存功能
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

这里写图片描述

那么我们再执行Executor.query的时候,也就是说先调用CachingExecutor的query方法

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //cachekey 即缓存的标识
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    //StatementId
    cacheKey.update(ms.getId());
    //自带分页参数数据
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    //sql语句
    cacheKey.update(boundSql.getSql());

    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
//xml中environment的id      cacheKey.update(configuration.getEnvironment().getId());
    }
    //以上种种,组成一个缓存标识
    return cacheKey;
  }
    public void update(Object object) {
    //生成hashcode,具体算法大家可以进去看,主要是分集合和非集合之分
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object); 

    count++;
    checksum += baseHashCode;
    //扩大count倍
    baseHashCode *= count;
//加上扩展固定值
    hashcode = multiplier * hashcode + baseHashCode;

    updateList.add(object);
  }

好了 有了key,那么就应该从缓存中获取数据了,

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
 ...省略
 //获取缓存
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
 ...判断是否为空
 //为空,则从数据库中获取
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
 ...省略
    return list;
  }
  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 {
    //每次都会移除 PerpetualCache  其实就是放到hashmap中
      localCache.removeObject(key);
    }
    //在put进缓存
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

好了 ,缓存是不是很简单,原来就是放到PerpetualCache类的hashmap中。

最后 session.close();关闭时,将localCache = null;缓存设置为null!!
也就是说 一级缓存的作用体现在同一个session中,一旦关闭,则清空,适合同一个session频繁的调用同样的sql(我想这种情况很少吧!)
而且,这种缓存的做法,默认状态是开启的,自动进行缓存,如果session长时间不关的话,会造成数据量越来越大,造成内存溢出!所以要及时关闭session哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值