您现在的位置是:主页 > news > 公司做的网站过期了/广州信息流推广公司

公司做的网站过期了/广州信息流推广公司

admin2025/6/8 19:43:21news

简介公司做的网站过期了,广州信息流推广公司,拖曳式网站建设,昆明中小企业网站建设1 二级缓存简介二级缓存是在多个SqlSession在同一个Mapper文件中共享的缓存,它是Mapper级别的,其作用域是Mapper文件中的namespace,默认是不开启的。看如下图:1.1 整个流程是这样的(不考虑第三方缓存库):当开启二级缓…

公司做的网站过期了,广州信息流推广公司,拖曳式网站建设,昆明中小企业网站建设1 二级缓存简介二级缓存是在多个SqlSession在同一个Mapper文件中共享的缓存,它是Mapper级别的,其作用域是Mapper文件中的namespace,默认是不开启的。看如下图:1.1 整个流程是这样的(不考虑第三方缓存库):当开启二级缓…

1  二级缓存简介

二级缓存是在多个SqlSession在同一个Mapper文件中共享的缓存,它是Mapper级别的,其作用域是Mapper文件中的namespace,默认是不开启的。看如下图:

26c6bd1e844ff9338acc8c703448a52e.png

1.1 整个流程是这样的(不考虑第三方缓存库):

当开启二级缓存后,在配置文件中配置这行代码,Mybatis会为SqlSession对象生成Executor对象时,还会生成一个对象:CachingExecutor,我们称之为装饰者,这里用到了装饰器模式。那么CachingExecutor的作用是什么呢?就是当一个查询请求过来时,CachingExecutor会接到请求,先进行二级缓存的查询,如果没命中,就交给真正的Executor(默认是SimpleExecutor,但是会调用它的父类BaseExecutor的query方法,因为要进行一级缓存的查询)来查询,再到一级缓存中查询,如果还没命中,再到数据库中查询。然后把查询到的结果再返回CachingExecutor,它进行二级缓存,最后再返回给请求方。它是executor的装饰者,增强executor的功能,具有查询缓存的作用。当配置时,请求过来时,BaseExecutor这个抽象类会接到请求,就不进行二级缓存的查询。

1.2 如何开启二级缓存,分三步:

一是在配置文件中开启,这是开启二级缓存的总开关,默认是开启状态的:

二是在Mapper文件中开启缓存,默认是不开启的,需要手动开启:

三是针对要查询的statement使用缓存,即在节点中配置如下属性:

useCache="true"

对于二级缓存有以下说明:

映射语句文件中的所有 select 语句将会被缓存。

映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。

缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。

根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。

缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。

缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

2 二级缓存存储取出清除过程:

2.1 二级缓存相关类的讲解:

在讲解二级缓存的存储取出清除的过程前,先了解下以下几个类:

2.1.1 TransactionalCache

TransactionalCache和TransactionalCacheManager是CachingExecutor依赖的两个组件。TransactionalCache实现了Cache接口,作用是保存某个sqlSession的某个事务中需要向某个二级缓存中添加的缓存数据,换句话说就是:某些缓存数据会先保存在这里,然后再提交到二级缓存中。源码如下:

public class TransactionalCache implementsCache {privateCache delegate;// 底层封装的二级缓存所对应的Cache对象,用到了装饰器模式 如下图1-1private booleanclearOnCommit; // 该字段为true时,则表示当前TransactionalCache不可查询,且提交事务时,会将底层的Cache清空

// 暂时记录添加都TransactionalCahce中的数据,在事务提交时,会将其中的数据添加到二级缓存中private MapentriesToAddOnCommit;private MapentriesToRemoveOnCommit;publicTransactionalCache(Cache delegate) {this.delegate =delegate;this.clearOnCommit = false;this.entriesToAddOnCommit = new HashMap();this.entriesToRemoveOnCommit = new HashMap();

}// 查询底层的二级缓存

@OverridepublicObject getObject(Object key) {if (clearOnCommit) return null; //issue #146

returndelegate.getObject(key);

}// 该方法并没有直接将查询的结果对象存储到其封装的二级缓存Cache对象中,而是暂时保存到entriesToAddOnCommit集合中,在事务提交时才会将这些结果从entriesToAddOnCommit集合中添加到二级缓存中

@Overridepublic voidputObject(Object key, Object object) {

entriesToRemoveOnCommit.remove(key);

entriesToAddOnCommit.put(key,newAddEntry(delegate, key, object));

}

@OverridepublicObject removeObject(Object key) {

entriesToAddOnCommit.remove(key);

entriesToRemoveOnCommit.put(key,newRemoveEntry(delegate, key));returndelegate.getObject(key);

}

@Overridepublic voidclear() {

reset();

clearOnCommit= true;

}

// 事务提交时,先根据clearOnCommit字段的值决定是否清空二级缓存,然后将entriesToAddOnCommit集合中的结果对象保存到二级缓存中public voidcommit() {

// 事务提交前,清空二级缓存if(clearOnCommit) {

delegate.clear();

}else{for(RemoveEntry entry : entriesToRemoveOnCommit.values()) {

entry.commit();

}

}

// 将entriesToAddOnCOmmit集合中的结果对象添加到二级缓存中for(AddEntry entry : entriesToAddOnCommit.values()) {

entry.commit();

}

reset();

}private static classAddEntry {privateCache cache;privateObject key;privateObject value;publicAddEntry(Cache cache, Object key, Object value) {this.cache =cache;this.key =key;this.value =value;

}

// 将entriesToAddOnCOmmit集合中的结果对象添加到二级缓存中,准确的说是PerpetualCache类的HashMap中public voidcommit() {

cache.putObject(key, value);

}

}private static classRemoveEntry {privateCache cache;privateObject key;publicRemoveEntry(Cache cache, Object key) {this.cache =cache;this.key =key;

}public voidcommit() {

cache.removeObject(key);

}

}

}

79cf2322975b6969b1b50ed3a9b635c7.png

(图1-1)

2.1.2 TranactionalCacheManager

TransactionalCacheManager是用于管理二级缓存对象Cache和TransactionCache的,它定义有transactionalCaches属性,看它的源码部分:

private Map transactionalCaches = new HashMap();

2.2 二级缓存的存储和取出过程

为了说明二级缓存存储取出的整个过程,通过下面demo中代码的执行顺序来分析源码:

@Testpublic voidselectGoodsTest(){ // 分三步进行源码分析:

SqlSession sqlSession = getSqlSessionFactory().openSession(true);GoodsDao goodsMapper= sqlSession.getMapper(GoodsDao.class);

GoodsDao goodsMapper2= sqlSession.getMapper(GoodsDao.class);// 第一步:第一次查询

goodsMapper.selectGoodsById("1");

// 第二步:事务提交

sqlSession.commit();// 第三步:第二次查询

goodsMapper2.selectGoodsById("1");

}

2.2.1 第一步:第一次查询

当配置二级缓存时,CachingExecutor会接到请求,调用它的query方法:先进行二级缓存的查询,如果没命中,再由BaseExecutor的query方法查询。看源码:

public Listquery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throwsSQLException {

// 进行二级缓存的查询

// 此处的cache就是当mybatis初始化加载mapper映射文件时,如果配置了,就会有该cache对象;下面会对MappedStatement这个类进行分析

Cache cache=ms.getCache();if (cache != null) {

//是否需要刷新缓存,默认情况下,select不需要刷新缓存,insert,delete,update要刷新缓存

flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {

ensureNoOutParams(ms, parameterObject, boundSql);

@SuppressWarnings("unchecked")

// 查询二级缓存,二级缓存是存放在PerpetualCache类中的HashMap中的,使用到了装饰器模式 分析此方法

List list = (List) tcm.getObject(cache, key);if (list == null) {

// 如果二级缓存没命中,则调用这个方法:这方法中是先查询一级缓存,如果还没命中,则会查询数据库

list= delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

// 把查询出的数据放到TransactionCache的entriesToAddOnCommit这个HashMap中,要注意,只是暂时存放到这里,只有当事务提交后,这里的数据才会真正的放到二级缓存中,后面会介绍这个 分析此方法

tcm.putObject(cache, key, list);//issue #578. Query must be not synchronized to prevent deadlocks

}returnlist;

}

}

// 如果不使用缓存,则调用BaseExecutor的方法return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}

在这个过程中,有两个方法需要分析:首先是查询二级缓存的方法tcm.getObject,通过跟踪源码最终发现,查询二级缓存是从PerpetualCache类的HashMap中获取数据的,也就是说二级缓存真正存放到了这个地方。另外一个处理查询出的数据tcm.putObject这个方法,这个方法最终是把查询出的数据存放到了TransactionalCache这个类中的HashMap中,以Cache接口的对象为key,查询结果集的映射对象为value。到这里,需要明白一点:在执行查询操作时,查询二级缓存的地点和存储查询数据的地点是不相同的。为什么是这样呢?这就引出了第二步,sqlSession.commit事务提交这个过程。

2.2.2 第二步:事务提交

现在我们知道,在第一次查询的时候,会把从数据库中查询的数据放到TransactionCache中,但这里并不是二级缓存存放数据的地方,那么二级缓存的数据什么时候怎么来的呢?这就要分析sqlSession.commit()这个方法了,这个方法就是把之前存放在TransactionCache中的数据提交到二级缓存中,然后清空该数据。通过源码,看下commit方法到底做了哪些事情?进入CachingExecutor的commit方法:

public void commit(boolean required) throwsSQLException {

// 清除一级缓存,执行缓存的SQL

delegate.commit(required);

// 将存放在TransactionCache中的数据对象提交到PerpetualCache中 进入此方法

tcm.commit();

}

进入TransactionalCacheManager类的commit方法:

public voidcommit() {

// 把涉及到的TransactionCache都进行处理:提交到二级缓存,并清空数据for(TransactionalCache txCache : transactionalCaches.values()) {

txCache.commit(); // 进入该方法 }

}

public voidcommit() {if(clearOnCommit) {

delegate.clear();

}else{for(RemoveEntry entry : entriesToRemoveOnCommit.values()) {

entry.commit();

}

}

// 把之前存放到entiriesToAddOnCommit中的数据提交到二级缓存中,具体的说是存放到PerpetualCache类的一个HashMap中for(AddEntry entry : entriesToAddOnCommit.values()) {

// 进入该方法

entry.commit();

}

//清空该TransactionCache中的数据

reset();

}

进入entry.commit()方法:

public voidcommit() {

cache.putObject(key, value); //放到PerpetualCache类中的HashMap中

}

到这里二级缓存的原理应该理解个大概了,总结下:当第一次从数据库中查出数据后,会放到TransactionCache类中;当调用sqlSession.commit()方法,进行事务提交后,TransactionCache中的数据会提交到PerpetualCache中,查询二级缓存的数据就是在这个类中,同时,TransactionCache中的数据会清空。

2.2.3 第三步:第二次查询

在事务提交之后,数据结果集对象就存放在了二级缓存中,所以第二次查询时,就可以从二级缓存中查询到数据了。进入TransactionalCache的getObject方法:

@OverridepublicObject getObject(Object key) {if (clearOnCommit) return null; //issue #146

// 用到了装饰器模式,从PerpetualCache中取出数据

returndelegate.getObject(key);

}

2.3 二级缓存的清除过程

先运行以下demo:

public classGoodsDaoTest {private static SqlSessionFactory sqlSessionFactory = null;

@Testpublic voidselectGoodsTest(){

SqlSession sqlSession= getSqlSessionFactory().openSession(true);

SqlSession sqlSession2= getSqlSessionFactory().openSession(true);

SqlSession sqlSession3= getSqlSessionFactory().openSession(true);

GoodsDao goodsMapper= sqlSession.getMapper(GoodsDao.class) ;

GoodsDao goodsMapper2= sqlSession2.getMapper(GoodsDao.class) ;

GoodsDao goodsMapper3= sqlSession3.getMapper(GoodsDao.class) ;

goodsMapper.selectGoodsById("1");

sqlSession.commit();

Goods goods= newGoods();

goods.setName("java1");

goods.setId("1");

goodsMapper3.updateGoodsById(goods); // 第一步 更新操作

sqlSession3.commit(); // 第二步 提交事务

goodsMapper2.selectGoodsById("1");

}public staticSqlSessionFactory getSqlSessionFactory() {

String resource= "spring-ibatis.xml";if(sqlSessionFactory == null){try{

sqlSessionFactory= newSqlSessionFactoryBuilder().build(Resources

.getResourceAsReader(resource));

}catch(IOException e) {//TODO Auto-generated catch block

e.printStackTrace();

}

}returnsqlSessionFactory;

}

}

看日志:

==> Preparing: select * from goods WHERE id = ?

==> Parameters: 1(String)<==Columns: id, name, price, detail, remark<== Row: 1, java1, 30.00, null, null

<== Total: 1Opening JDBC Connection

Created connection1998228836.==> Preparing: update goods set name = ? where id = ?

==> Parameters: java1(String), 1(String)<== Updates: 1Cache Hit Ratio [com.yht.mybatisTest.dao.GoodsDao]:0.0Opening JDBC Connection

Created connection1945928717.==> Preparing: select * from goods WHERE id = ?

==> Parameters: 1(String)<==Columns: id, name, price, detail, remark<== Row: 1, java1, 30.00, null, null

<== Total: 1

总结:在更新操作,并提交事务后,清除了二级缓存,所以第二次查询时,是从数据库中查询的数据。接下来,就针对更新操作和提交事务这两个过程作分析。

2.3.1 第一步 更新操作

进入CachingExecutor类的update方法:

public int update(MappedStatement ms, Object parameterObject) throwsSQLException {

// 进入该方法,可知:清空了TransactionalCache中entriesToAddOnCommit和entriesToRemoveOnCommit的数据,同时clearOnCommit设置为true

flushCacheIfRequired(ms);returndelegate.update(ms, parameterObject);

但是二级缓存中的数据对象并未清除,所以进入第二步事务提交。

2.3.2 第二步 事务提交

最终进入TransactionalCache的commit方法:

public voidcommit() {if(clearOnCommit) {

// 由于在上一步更新操作中,clearOnCommit设置为了true,所以进入此方法:清除二级缓存中的数据

delegate.clear();

}else{for(RemoveEntry entry : entriesToRemoveOnCommit.values()) {

entry.commit();

}

}for(AddEntry entry : entriesToAddOnCommit.values()) {

entry.commit();

}

reset();

}

这就是清除二级缓存的过程。

总结一下:其实主要就是把这几个类之间的关系及其作用搞清楚就行了:CachingExecutor,BaseExecutor,SimpleExecutor和TransactionalCache,PerpetualCache。这几个类也是整个请求过程中比较重要的类。