MyBatis默认是支持一级缓存和二级缓存,他自身的实现是通过PerpetualCache,这哥缓存对象实现了,当然也可以是MemarCache。目前中用的比较多的就是Redis,是一个nosql数据库,也称为缓存数据库。
1. 什么是一级缓存? 为什么使用一级缓存?
每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话。
在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,如果不采取一些措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。
为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了。
如下图所示,MyBatis会在一次会话的表示----一个SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询结果存入缓存并返回给用户。
这个图很好的说明了MyBatis一级缓存的使用周期,以及过程。
Mybatis一级缓存的工作过程:
1.对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果;
2. 判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;
3. 如果命中,则直接将缓存结果返回;
4. 如果没命中:
4.1 去数据库中查询数据,得到查询结果;
4.2 将key和查询到的结果分别作为key,value对存储到Cache中;
4.3. 将查询结果返回;
5. 结束。接下来是和Redis整合的例子:
Redis可以说是第三方缓存数据库,很强大,这样就会涉及到二级缓存,二级缓存的图如下:
继承Cache接口:
- package com.lgy.cache;
- import java.util.concurrent.locks.ReadWriteLock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- import org.apache.ibatis.cache.Cache;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.lgy.util.SerializeUtil;
- import com.lgy.util.redis.JedisPoolUtils;
- import redis.clients.jedis.Jedis;
- public class MybatisRedisCache implements Cache {
- private static Logger logger = LoggerFactory.getLogger(MybatisRedisCache.class);
- private Jedis redisClient = JedisPoolUtils.getPool().getResource();
- /** The ReadWriteLock. */
- private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
- private String id;
- public MybatisRedisCache(final String id) {
- if (id == null) {
- throw new IllegalArgumentException("Cache instances require an ID");
- }
- logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>MybatisRedisCache:id=" + id);
- this.id = id;
- }
- @Override
- public String getId() {
- return this.id;
- }
- @Override
- public int getSize() {
- return Integer.valueOf(redisClient.dbSize().toString());
- }
- @Override
- public void putObject(Object key, Object value) {
- logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>putObject:" + key + "=" + value);
- redisClient.set(SerializeUtil.serialize(key.toString()), SerializeUtil.serialize(value));
- }
- @Override
- public Object getObject(Object key) {
- Object value = null;
- byte[] bs = redisClient.get(SerializeUtil.serialize(key.toString()));
- if(bs != null) {
- value = SerializeUtil.unserialize(bs);
- }
- logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>getObject:" + key + "=" + value);
- return value;
- }
- @Override
- public Object removeObject(Object key) {
- logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>removeObject:key=" + key);
- return redisClient.expire(SerializeUtil.serialize(key.toString()), 0);
- }
- @Override
- public void clear() {
- logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>clear");
- redisClient.flushDB();
- }
- @Override
- public ReadWriteLock getReadWriteLock() {
- logger.debug(">>>>>>>>>>>>>>>>>>>>>>>>getReadWriteLock");
- return readWriteLock;
- }
- }
注意几个特别的方法:
1.getObject
2.putObject
3.clear
1.第一次调用select语句时候
a.会执行getObject()方法,从缓存中获取,若获取到了直接返回。
b.活执行getObject()方法,从缓存中获取,为null则会执行select sql语句,然后调用putObject方法将key-value存在缓存服务器中。
2.调用update语句,会清空db,调用clear方法
Mybatis实现缓存的机制使用了大量的装饰者模式,有兴趣同学可以去研究下!
转载于:https://blog.csdn.net/qq383264679/article/details/49798807
备注:https://blog.csdn.net/qq_25689397/article/details/52066179,这篇文章通过源码分析更加详情的讲解了mybatis中整合redis