mybatis之缓存模块

mybatis之缓存模块

mybatis中存在一级(默认开启)、二级缓存。缓存可以加快查询速度,减少连接db的次数。

cache模块属于mybatis的基础支持层。位于org.apache.ibatis.cache包下。

--------org.apache.ibatis.cache
-------------------------------decorators
-----------------------------------------BlockingCache
-----------------------------------------FifoCache
-----------------------------------------LoggingCache
-----------------------------------------LruCache
-----------------------------------------ScheduledCache
-----------------------------------------SerializedCache
-----------------------------------------SoftCache
-----------------------------------------SynchronizedCache
-----------------------------------------TransactionalCache
-----------------------------------------WeakCache
-------------------------------impl
-----------------------------------PerpetualCache
------------------------------Cache

Cache PerpetualCache BlockingCache FifoCache LoggingCache LruCache ScheduledCache SerializedCache SoftCache SynchronizedCache TransactionalCache WeakCache

Cache

缓存的顶级接口

public interface Cache {
    // 得到缓存的id
    String getId();
    // 插入缓存
    void putObject(Object key, Object value);
    // 查询到缓存
    Object getObject(Object key);
    // 删除缓存
    Object removeObject(Object key);
    //清理这个缓存实例
    void clear();
    // 这个方法可选,不是核心调用方法,查询缓存key的个数
    int getSize();
    //
    ReadWriteLock getReadWriteLock();
}   

PerpetualCache

缓存的基础实现(实现了基本的缓存功能,底层数据结构使用HashMap)

一级缓存是会话级别的缓存(SqlSession)。

public class PerpetualCache implements Cache {

    private final String id;
    // 存储缓存底层数据结构使用hashMap
    private Map<Object, Object> cache = new HashMap<Object, Object>();
    // 传入
    public PerpetualCache(String id) {
        this.id = id;
    }

    @Override
    public String getId() {
        return id;
    }


    @Override
    public int getSize() {
        return cache.size();
    }

    // 插入缓存
    @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);
    }

    @Override
    public void clear() {
        cache.clear();
    }

      @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }
}

BlockingCache

BlockingCache是带有阻塞功能的缓存装饰器。

public class BlockingCache implements Cache {

    //超时时间
    private long timeout;
    // 被装饰的缓存
    private final Cache delegate;
    // 每一个Key拥有一把可重入锁
    private final ConcurrentHashMap<Object, ReentrantLock> locks;
    //构造器,装饰其他cache对象
    public BlockingCache(Cache delegate) {
      this.delegate = delegate;
      this.locks = new ConcurrentHashMap<Object, ReentrantLock>();
    }

    @Override
    public String getId() {
      return delegate.getId();
    }

    @Override
    public int getSize() {
      return delegate.getSize();
    }

    // 插入Key的缓存,插入完毕同时释放锁
    @Override
    public void putObject(Object key, Object value) {
      try {
        delegate.putObject(key, value);
      } finally {
        releaseLock(key);
      }
    }

  // 查询key的缓存
  @Override
  public Object getObject(Object key) {
    //获取到key的锁
    acquireLock(key);
    // 查询key缓存的值
    Object value = delegate.getObject(key);
    //如果value不为null,则释放锁,返回值,否则一直持有锁,知道key有值插入到缓存
    if (value != null) {
      releaseLock(key);
    }        
    return value;
  }

  @Override
  public Object removeObject(Object key) {
    // despite of its name, this method is called only to release locks
    releaseLock(key);
    return null;
  }

  @Override
  public void clear() {
    delegate.clear();
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  
  //查询Key持有的锁,如果没有则创建新锁
  private ReentrantLock getLockForKey(Object key) {
    ReentrantLock lock = new ReentrantLock();
    ReentrantLock previous = locks.putIfAbsent(key, lock);
    return previous == null ? lock : previous;
  }
  
  private void acquireLock(Object key) {
    Lock lock = getLockForKey(key);
    if (timeout > 0) {
      try {
        boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
        if (!acquired) {
          throw new CacheException("Couldn't get a lock in " + timeout + " for the key " +  key + " at the cache " + delegate.getId());  
        }
      } catch (InterruptedException e) {
        throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
      }
    } else {
      lock.lock();
    }
  }
  
  private void releaseLock(Object key) {
    ReentrantLock lock = locks.get(key);
    if (lock.isHeldByCurrentThread()) {
      lock.unlock();
    }
  }

  public long getTimeout() {
    return timeout;
  }

  public void setTimeout(long timeout) {
    this.timeout = timeout;
  } 


}

线程A第一次查询key,结果不存在缓存,A持有key的锁; 同时线程B查询key,此时线程B被阻塞,A去数据查询获取到数据缓存起来释放key的锁,B被唤醒继续从缓存查询。

BlockingCache会装饰PerpetualCache缓存。

装饰者模式

从上面缓存的包结构上我们可以看到,Cache是顶层接口,PerpetualCache是基本实现,decorators下的各个类是不同功能的缓存器,通过装饰者模式来实现不同功能的组合。

//顶测接口
public interface  Cache{

  Object getObject(Object key);

}

//基本实现类
public class PerpetualCache implements  Cache {
    
    @Override
    public Object getObject(Object key) {
      return cache.get(key);
    }
}

//装饰者
public class BlockingCache implements Cache {
  
  @Override
  public Object getObject(Object key) {
      acquireLock(key);
      Object value = delegate.getObject(key);
      if (value != null) {
        releaseLock(key);
      }        
      return value;
  }

}

//示例
public class Example {

    public static void main() {
        Cache cache = new PerpetualCache();
        //装饰缓存,增加锁的功能
        cache = new BlockingCache(cache);
        CacheKey cacheKey = new CacheKey();
        cache.getOject(cacheKey); 
    }
}

CacheKey

由于mybati支持动态sql,因此不能通过一个简单的string来构造缓存的key,CacheKey就是Cache中的key。

//重写了Equals与hashCode方法
public class CacheKey implements Cloneable, Serializable {
private static final long serialVersionUID = 1146682552656046210L;

  public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();
  //multiplier的默认值
  private static final int DEFAULT_MULTIPLYER = 37;
  //hashcode的默认值
  private static final int DEFAULT_HASHCODE = 17;
  //用来计算hashcode值
  private final int multiplier;
  private int hashcode;
  private long checksum;
  //对象的个数
  private int count;
  //存储构成CacheKey的对象
  private transient List<Object> updateList;

  public CacheKey() {
    this.hashcode = DEFAULT_HASHCODE;
    this.multiplier = DEFAULT_MULTIPLYER;
    this.count = 0;
    this.updateList = new ArrayList<Object>();
  }

  public CacheKey(Object[] objects) {
    this();
    updateAll(objects);
  }

  public int getUpdateCount() {
    return updateList.size();
  }

  //存储构成CacheKey的对象,以及重新计算hashcode
  public void update(Object object) {
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object); 

    count++;
    checksum += baseHashCode;
    baseHashCode *= count;
    //重新计算hashcode
    hashcode = multiplier * hashcode + baseHashCode;
    //存储对象
    updateList.add(object);
  }

  public void updateAll(Object[] objects) {
    for (Object o : objects) {
      update(o);
    }
  }

  @Override
  public boolean equals(Object object) {
    if (this == object) {
      return true;
    }
    if (!(object instanceof CacheKey)) {
      return false;
    }

    final CacheKey cacheKey = (CacheKey) object;

    if (hashcode != cacheKey.hashcode) {
      return false;
    }
    if (checksum != cacheKey.checksum) {
      return false;
    }
    if (count != cacheKey.count) {
      return false;
    }

    for (int i = 0; i < updateList.size(); i++) {
      Object thisObject = updateList.get(i);
      Object thatObject = cacheKey.updateList.get(i);
      if (!ArrayUtil.equals(thisObject, thatObject)) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    return hashcode;
  }

  @Override
  public String toString() {
    StringBuilder returnValue = new StringBuilder().append(hashcode).append(':').append(checksum);
    for (Object object : updateList) {
      returnValue.append(':').append(ArrayUtil.toString(object));
    }
    return returnValue.toString();
  }

  @Override
  public CacheKey clone() throws CloneNotSupportedException {
    CacheKey clonedCacheKey = (CacheKey) super.clone();
    clonedCacheKey.updateList = new ArrayList<Object>(updateList);
    return clonedCacheKey;
  }

}

CacheKey是由以下5个部分构成的

1.MappedStatement的id
2.指定查询结果的范围也就是rowBounds.Offset,rowBounds.limit
3.查询所使用的包含?的动态sql
4.用户传递的实际参数
5.配置的环境id,如果mybatis-config.xml有配置环境信息的话。

public abstract class BaseExecutor implements Executor {
//创建CacheKey
@Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    //MappedStatement的id
    cacheKey.update(ms.getId());
    // 指定查询结果的范围也就是rowBounds.Offset,rowBounds.limit
    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
      // 配置的环境id,如果mybatis-config.xml有配置环境信息的话。
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }
}

总结

1.mybatis中存在一级(默认开启)、二级缓存。缓存可以加快查询速度,减少连接db的次数。
2.cache模块属于mybatis的基础支持层。位于org.apache.ibatis.cache包下。
3.PerpetualCache缓存的基础实现(实现了基本的缓存功能,底层数据结构使用HashMap)
4.BlockingCache是带有阻塞功能的缓存装饰器。
5.缓存模块的设计采用了装饰者模式
6.Cache是顶层接口,PerpetualCache是基本实现,decorators下的各个类是不同功能的缓存器,通过装饰者模式来实现不同功能的组合。
7.CacheKey是由5个部分构成的:mappedStatement的id、指定查询结果的范围、查询所使用的包含?的动态sql、用户传递的实际参数、配置的环境id

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值