mybatis学习分享2

mybatis缓存

一级缓存

一级缓存是对于同一个SqlSession
mybatis默认开启一级缓存, SqlSession将查询任务委托给执行器Executor,执行器Executor中的PerpetualCache对象用来存放一级缓存数据。
PerpetualCache底层采用HashMap实现,key是查询的标识,value即是查询结果。
一次查询操作中:生成此次查询的标识key,根据key获取value即一级缓存数据,如果不为空,则直接返回缓存数据;如果为空,则从数据库中获取数据,并将数据放到PerpetualCache中。

1、PerpetualCache的key:
在类BaseExecutor中,实现了接口Executor中的createCacheKey方法:

public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
	......
	CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    ......
    // value指的是处理过的查询参数
    cacheKey.update(value);
    ......
    // Environment是mybatis的运行环境,可以为mybatis配置多个运行环境,根据Environment的Id进行区分:https://blog.csdn.net/u014268482/article/details/80595602
	if (this.configuration.getEnvironment() != null) {
		cacheKey.update(this.configuration.getEnvironment().getId());
	}
	return cacheKey;
}

由此,我们可以看出,mybatis根据下列信息生成key:

  1. MappedStatement的Id
  2. 查询范围
  3. sql
  4. 参数
  5. mybatis的运行环境id

2、一级缓存生命周期

  1. 开启会话,创建一个新的SqlSession对象,SqlSession对象中有一个Executor对象,Executor对象中有一个PerpetualCache对象。
  2. 会话结束或SqlSession调用close()方法,释放SqlSession对象,同时释放Executor对象、PerpetualCache对象。
  3. SqlSession调用clearCache()方法或执行了update()、delete()、insert()方法,清空PerpetualCache对象,但是还可以再次使用。

3、一级缓存和spring事务的关系
一般来说,每次查询后,一级缓存就会被清空掉。但是如果加上了spring事务,每次查询后,一级缓存不会被清空掉,当事务结束或者有新增、更新、删除操作时,一级缓存会被清空。

二级缓存

二级缓存是对于同一个map接口。

springboot开启二级缓存配置

1、配置文件开启mybatis二级缓存,默认开启

mybatis:
  configuration:
    cache-enabled: true

2、在mapper.xml中加入如下代码

<cache/>

cache的属性

MappedStatement中有一个Cache属性,如果mapper.xml中没有<cache/>,则此属性为null;如果在mapper.xml中加入<cache/>后,此mapper.xml的二级缓存相关信息会封装到Cache属性中,Cacheid就是mapper.xml的namespace,即map接口全路径名。
如果多个mapper.xml对应同一个map接口,那么这些mapper.xml对应的MappedStatement中的Cache属性是一样的。

原理

二级缓存采用装饰者模式,当开启二级缓存后,执行器采用CachingExecutor

查询逻辑
CachingExecutorquery方法:

public class CachingExecutor implements Executor {
	private final Executor delegate;
	// 事务缓存管理器
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();
	......
	/**
     * 查询代码
     *
     * @param key 和一级缓存生成的key方法一样
     * @return 查询结果
     */
	public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
		// 1、判断此mapper.xml是否开启了二级缓存
        Cache cache = ms.getCache();
        if (cache != null) {
            ......
            // 1.1、从二级缓存中获取数据
            List<E> list = (List)this.tcm.getObject(cache, key);
            if (list == null) {
            	// 1.1.1、二级缓存中无此数据,走一级缓存查询逻辑
                list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                // 1.1.2、将数据放到二级缓存中
                this.tcm.putObject(cache, key, list);
            }
            return list;
        }
        // 2、此mapper.xml未开启二级缓存,走一级缓存查询逻辑
        return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
}

TransactionalCacheManager

public class TransactionalCacheManager {
	// 事务缓存map
    private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap();

    public TransactionalCacheManager() {
    }

    public void clear(Cache cache) {
        this.getTransactionalCache(cache).clear();
    }

	/**
     * 获取事务缓存中的缓存
     */
    public Object getObject(Cache cache, CacheKey key) {
        return this.getTransactionalCache(cache).getObject(key);
    }

	/**
     * 增加事务缓存中的缓存
     */
    public void putObject(Cache cache, CacheKey key, Object value) {
        this.getTransactionalCache(cache).putObject(key, value);
    }

	/**
     * 提交数据
     */
    public void commit() {
        Iterator var1 = this.transactionalCaches.values().iterator();

        while(var1.hasNext()) {
            TransactionalCache txCache = (TransactionalCache)var1.next();
            txCache.commit();
        }

    }

    public void rollback() {
        Iterator var1 = this.transactionalCaches.values().iterator();

        while(var1.hasNext()) {
            TransactionalCache txCache = (TransactionalCache)var1.next();
            txCache.rollback();
        }

    }

	/**
     * 获取事务缓存
     */
    private TransactionalCache getTransactionalCache(Cache cache) {
        TransactionalCache txCache = (TransactionalCache)this.transactionalCaches.get(cache);
        if (txCache == null) {
            txCache = new TransactionalCache(cache);
            this.transactionalCaches.put(cache, txCache);
        }

        return txCache;
    }
}

TransactionalCache

public class TransactionalCache implements Cache {
    private static final Log log = LogFactory.getLog(TransactionalCache.class);
    // 缓存
	private final Cache delegate;
    private boolean clearOnCommit;
	// 需要提交到缓存中的数据
	private final Map<Object, Object> entriesToAddOnCommit;
	// 缓存中没有的数据的key集合
	private final Set<Object> entriesMissedInCache;

    public TransactionalCache(Cache delegate) {
        this.delegate = delegate;
        this.clearOnCommit = false;
        this.entriesToAddOnCommit = new HashMap();
        this.entriesMissedInCache = new HashSet();
    }

    public String getId() {
        return this.delegate.getId();
    }

    public int getSize() {
        return this.delegate.getSize();
    }

	/**
     * 获取二级缓存数据
     */
    public Object getObject(Object key) {
        // 从二级缓存中获取数据
        Object object = this.delegate.getObject(key);
        if (object == null) {
        	// 二级缓存中无此数据,将key暂时放到内存中
            this.entriesMissedInCache.add(key);
        }

        return this.clearOnCommit ? null : object;
    }

    public ReadWriteLock getReadWriteLock() {
        return null;
    }

	/**
     * 将数据暂时存到内存中
     */
    public void putObject(Object key, Object object) {
        this.entriesToAddOnCommit.put(key, object);
    }

    public Object removeObject(Object key) {
        return null;
    }

    public void clear() {
        this.clearOnCommit = true;
        this.entriesToAddOnCommit.clear();
    }

	/**
     * 提交数据
     */
    public void commit() {
        if (this.clearOnCommit) {
            this.delegate.clear();
        }

        this.flushPendingEntries();
        this.reset();
    }

    public void rollback() {
        this.unlockMissedEntries();
        this.reset();
    }

    private void reset() {
        this.clearOnCommit = false;
        this.entriesToAddOnCommit.clear();
        this.entriesMissedInCache.clear();
    }

	/**
     * 将内存中的entriesToAddOnCommit、entriesMissedInCache数据推到缓存中
     */
    private void flushPendingEntries() {
        Iterator var1 = this.entriesToAddOnCommit.entrySet().iterator();

        while(var1.hasNext()) {
            Entry<Object, Object> entry = (Entry)var1.next();
            this.delegate.putObject(entry.getKey(), entry.getValue());
        }

        var1 = this.entriesMissedInCache.iterator();

        while(var1.hasNext()) {
            Object entry = var1.next();
            if (!this.entriesToAddOnCommit.containsKey(entry)) {
                this.delegate.putObject(entry, (Object)null);
            }
        }

    }

    private void unlockMissedEntries() {
        Iterator var1 = this.entriesMissedInCache.iterator();

        while(var1.hasNext()) {
            Object entry = var1.next();

            try {
                this.delegate.removeObject(entry);
            } catch (Exception var4) {
                log.warn("Unexpected exception while notifiying a rollback to the cache adapter.Consider upgrading your cache adapter to the latest version.  Cause: " + var4);
            }
        }

    }
}

当查询结束以后,会执行commit()将内存中的事务缓存数据推到二级缓存中。

二级缓存缺陷

如果一张表在多个mapper.xml中出现,一个mapper.xml因为更新表刷新了此mapper.xml的二级缓存,此时其他mapper.xml的二级缓存是没有刷新的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值