做电商网站一般需要什么流程图,房城乡建设部门户网站,信息流优化师没经验可以做吗,广州互邦物流网络优化建站转载自  MyBatis】MyBatis一级缓存和二级缓存 
MyBatis自带的缓存有一级缓存和二级缓存 
一级缓存 
Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。  也就是在同一个SqlSession中#xff0c;执行相同的查询SQL#xff…转载自  MyBatis】MyBatis一级缓存和二级缓存 
MyBatis自带的缓存有一级缓存和二级缓存 
一级缓存 
Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。  也就是在同一个SqlSession中执行相同的查询SQL第一次会去数据库进行查询并写到缓存中  第二次以后是直接去缓存中取。  当执行SQL查询中间发生了增删改的操作MyBatis会把SqlSession的缓存清空。 
一级缓存的范围有SESSION和STATEMENT两种默认是SESSION如果不想使用一级缓存可以把一级缓存的范围指定为STATEMENT这样每次执行完一个Mapper中的语句后都会将一级缓存清除。  如果需要更改一级缓存的范围可以在Mybatis的配置文件中在下通过localCacheScope指定。 setting namelocalCacheScope valueSTATEMENT/ 
建议不需要修改 
需要注意的是  当Mybatis整合Spring后直接通过Spring注入Mapper的形式如果不是在同一个事务中每个Mapper的每次查询操作都对应一个全新的SqlSession实例这个时候就不会有一级缓存的命中但是在同一个事务中时共用的是同一个SqlSession。  如有需要可以启用二级缓存。 
二级缓存 
Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容多个SqlSession共享。Mybatis需要手动设置启动二级缓存。 
二级缓存是默认启用的(要生效需要对每个Mapper进行配置)如想取消则可以通过Mybatis配置文件中的元素下的子元素来指定cacheEnabled为false。 
settingssetting namecacheEnabled valuefalse /
/settings 
cacheEnabled默认是启用的只有在该值为true的时候底层使用的Executor才是支持二级缓存的CachingExecutor。具体可参考Mybatis的核心配置类org.apache.ibatis.session.Configuration的newExecutor方法实现。  可以通过源码看看 
...    public Executor newExecutor(Transaction transaction) {        return this.newExecutor(transaction, this.defaultExecutorType);}    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType  executorType  null ? this.defaultExecutorType : executorType;executorType  executorType  null ? ExecutorType.SIMPLE : executorType;Object 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);}        if (this.cacheEnabled) {//设置为true才执行的executor  new CachingExecutor((Executor)executor);}Executor executor  (Executor)this.interceptorChain.pluginAll(executor);        return executor;}
... 
要使用二级缓存除了上面一个配置外我们还需要在我们每个DAO对应的Mapper.xml文件中定义需要使用的cache 
...
mapper namespace...UserMappercache/!-- 加上该句即可使用默认配置、还有另外一种方式在后面写出 --...
/mapper 
具体可以看org.apache.ibatis.executor.CachingExecutor类的以下实现  其中使用的cache就是我们在对应的Mapper.xml中定义的cache。 public E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql  ms.getBoundSql(parameterObject);CacheKey key  this.createCacheKey(ms, parameterObject, rowBounds, boundSql);        return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}    public E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {Cache cache  ms.getCache();        if (cache ! null) {//第一个条件 定义需要使用的cache  this.flushCacheIfRequired(ms);            if (ms.isUseCache()  resultHandler  null) {//第二个条件 需要当前的查询语句是配置了使用cache的即下面源码的useCache()是返回true的  默认是truethis.ensureNoOutParams(ms, parameterObject, boundSql);ListE list  (List)this.tcm.getObject(cache, key);                if (list  null) {list  this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);                    this.tcm.putObject(cache, key, list);}                return list;}}        return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);} 
还有一个条件就是需要当前的查询语句是配置了使用cache的即上面源码的useCache()是返回true的默认情况下所有select语句的useCache都是true如果我们在启用了二级缓存后有某个查询语句是我们不想缓存的则可以通过指定其useCache为false来达到对应的效果。  如果我们不想该语句缓存可使用useCache”false 
select idselectByPrimaryKey resultMapBaseResultMap parameterTypejava.lang.String useCachefalseselectinclude refidBase_Column_List/from tuserwhere id  #{id,jdbcTypeVARCHAR}/select 
cache定义的两种使用方式 
上面说了要想使用二级缓存需要在每个DAO对应的Mapper.xml文件中定义其中的查询语句需要使用cache来缓存数据的。  这有两种方式可以定义一种是通过cache元素定义一种是通过cache-ref元素来定义。  需要注意的是  对于同一个Mapper来讲只能使用一个Cache当同时使用了和时定义的优先级更高(后面的代码会给出原因)。  Mapper使用的Cache是与我们的Mapper对应的namespace绑定的一个namespace最多只会有一个Cache与其绑定。 
cache元素定义 
使用cache元素来定义使用的Cache时最简单的做法是直接在对应的Mapper.xml文件中指定一个空的元素(看前面的代码)这个时候Mybatis会按照默认配置创建一个Cache对象准备的说是PerpetualCache对象更准确的说是LruCache对象底层用了装饰器模式。  具体的可看org.apache.ibatis.builder.xml.XMLMapperBuilder中的cacheElement()方法解析cache元素的逻辑。 
...    private void configurationElement(XNode context) {        try {String namespace  context.getStringAttribute(namespace);            if (namespace.equals()) {                throw new BuilderException(Mappers namespace cannot be empty);} else {                this.builderAssistant.setCurrentNamespace(namespace);                this.cacheRefElement(context.evalNode(cache-ref));                this.cacheElement(context.evalNode(cache));//执行在后面this.parameterMapElement(context.evalNodes(/mapper/parameterMap));                this.resultMapElements(context.evalNodes(/mapper/resultMap));                this.sqlElement(context.evalNodes(/mapper/sql));                this.buildStatementFromContext(context.evalNodes(select|insert|update|delete));}} catch (Exception var3) {            throw new BuilderException(Error parsing Mapper XML. Cause:   var3, var3);}}
...    private void cacheRefElement(XNode context) {        if (context ! null) {            this.configuration.addCacheRef(this.builderAssistant.getCurrentNamespace(), context.getStringAttribute(namespace));CacheRefResolver cacheRefResolver  new CacheRefResolver(this.builderAssistant, context.getStringAttribute(namespace));            try {cacheRefResolver.resolveCacheRef();} catch (IncompleteElementException var4) {                this.configuration.addIncompleteCacheRef(cacheRefResolver);}}}    private void cacheElement(XNode context) throws Exception {        if (context ! null) {String type  context.getStringAttribute(type, PERPETUAL);Class? extends Cache typeClass  this.typeAliasRegistry.resolveAlias(type);String eviction  context.getStringAttribute(eviction, LRU);Class? extends Cache evictionClass  this.typeAliasRegistry.resolveAlias(eviction);Long flushInterval  context.getLongAttribute(flushInterval);Integer size  context.getIntAttribute(size);            boolean readWrite  !context.getBooleanAttribute(readOnly, false).booleanValue();Properties props  context.getChildrenAsProperties();            this.builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);//如果同时存在cache和cache-ref这里的设置会覆盖前面的cache-ref的缓存}} 
空cache元素定义会生成一个采用最近最少使用算法最多只能存储1024个元素的缓存而且是可读写的缓存即该缓存是全局共享的任何一个线程在拿到缓存结果后对数据的修改都将影响其它线程获取的缓存结果因为它们是共享的同一个对象。 
cache元素可指定如下属性每种属性的指定都是针对都是针对底层Cache的一种装饰采用的是装饰器的模式。 blocking默认为false当指定为true时将采用BlockingCache进行封装blocking阻塞的意思使用BlockingCache会在查询缓存时锁住对应的Key如果缓存命中了则会释放对应的锁否则会在查询数据库以后再释放锁这样可以阻止并发情况下多个线程同时查询数据详情可参考BlockingCache的源码。  简单理解也就是设置true时在进行增删改之后的并发查询只会有一条去数据库查询而不会并发  evictioneviction驱逐的意思。也就是元素驱逐算法默认是LRU对应的就是LruCache其默认只保存1024个Key超出时按照最近最少使用算法进行驱逐详情请参考LruCache的源码。如果想使用自己的算法则可以将该值指定为自己的驱逐算法实现类只需要自己的类实现Mybatis的Cache接口即可。除了LRU以外系统还提供了FIFO先进先出对应FifoCache、SOFT采用软引用存储Value便于垃圾回收对应SoftCache和WEAK采用弱引用存储Value便于垃圾回收对应WeakCache这三种策略。  这里根据个人需求选择了没什么要求的话默认的LRU即可  flushInterval清空缓存的时间间隔单位是毫秒默认是不会清空的。当指定了该值时会再用ScheduleCache包装一次其会在每次对缓存进行操作时判断距离最近一次清空缓存的时间是否超过了flushInterval指定的时间如果超出了则清空当前的缓存详情可参考ScheduleCache的实现。  readOnly是否只读  默认为false。当指定为false时底层会用SerializedCache包装一次其会在写缓存的时候将缓存对象进行序列化然后在读缓存的时候进行反序列化这样每次读到的都将是一个新的对象即使你更改了读取到的结果也不会影响原来缓存的对象即非只读你每次拿到这个缓存结果都可以进行修改而不会影响原来的缓存结果  当指定为true时那就是每次获取的都是同一个引用对其修改会影响后续的缓存数据获取这种情况下是不建议对获取到的缓存结果进行更改意为只读(不建议设置为true)。  这是Mybatis二级缓存读写和只读的定义可能与我们通常情况下的只读和读写意义有点不同。每次都进行序列化和反序列化无疑会影响性能但是这样的缓存结果更安全不会被随意更改具体可根据实际情况进行选择。详情可参考SerializedCache的源码。  size用来指定缓存中最多保存的Key的数量。其是针对LruCache而言的LruCache默认只存储最多1024个Key可通过该属性来改变默认值当然如果你通过eviction指定了自己的驱逐算法同时自己的实现里面也有setSize方法那么也可以通过cache的size属性给自定义的驱逐算法里面的size赋值。  typetype属性用来指定当前底层缓存实现类默认是PerpetualCache如果我们想使用自定义的Cache则可以通过该属性来指定对应的值是我们自定义的Cache的全路径名称。 
cache-ref元素定义 
cache-ref元素可以用来指定其它Mapper.xml中定义的Cache有的时候可能我们多个不同的Mapper需要共享同一个缓存的  是希望在MapperA中缓存的内容在MapperB中可以直接命中的这个时候我们就可以考虑使用cache-ref这种场景只需要保证它们的缓存的Key是一致的即可命中二级缓存的Key是通过Executor接口的createCacheKey()方法生成的其实现基本都是BaseExecutor源码如下。 public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {        if (this.closed) {            throw new ExecutorException(Executor was closed.);} else {CacheKey cacheKey  new CacheKey();cacheKey.update(ms.getId());cacheKey.update(rowBounds.getOffset());cacheKey.update(rowBounds.getLimit());cacheKey.update(boundSql.getSql());ListParameterMapping parameterMappings  boundSql.getParameterMappings();TypeHandlerRegistry typeHandlerRegistry  ms.getConfiguration().getTypeHandlerRegistry();            for(int i  0; i  parameterMappings.size(); i) {ParameterMapping parameterMapping  (ParameterMapping)parameterMappings.get(i);                if (parameterMapping.getMode() ! ParameterMode.OUT) {String propertyName  parameterMapping.getProperty();Object value;                    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  this.configuration.newMetaObject(parameterObject);value  metaObject.getValue(propertyName);}cacheKey.update(value);}}            return cacheKey;}} 
打个比方我想在MenuMapper.xml中的查询都使用在UserMapper.xml中定义的Cache则可以通过cache-ref元素的namespace属性指定需要引用的Cache所在的namespace即UserMapper.xml中的定义的namespace假设在UserMapper.xml中定义的namespace是cn.chenhaoxiang.dao.UserMapper则在MenuMapper.xml的cache-ref应该定义如下。 
cache-ref namespacecn.chenhaoxiang.dao.UserMapper/ 1 
这样这两个Mapper就共享同一个缓存了 
自定义cache就不介绍了。 
测试二级缓存 查询测试 
/*** Created with IntelliJ IDEA.* User: 陈浩翔.* Date: 2018/1/10.* Time: 下午 10:15.* Explain:*/RunWith(SpringJUnit4ClassRunner.class)//配置了ContextConfiguration注解并使用该注解的locations属性指明spring和配置文件之后ContextConfiguration(locations  {classpath:spring.xml,classpath:spring-mybatis.xml})public class MyBatisTestBySpringTestFramework {//注入userServiceAutowiredprivate UserService userService;    Testpublic void testGetUserId(){String userId  4e07f3963337488e81716cfdd8a0fe04;User user  userService.getUserById(userId);System.out.println(user);        //前面说到spring和MyBatis整合User user2  userService.getUserById(userId);System.out.println(user2:user2);}
} 接下来我们把Mapper中的cache元素删除不使用二级缓存 再运行测试 对二级缓存进行了以下测试获取两个不同的SqlSession(前面有说Spring和MyBatis集成每次都是不同的SqlSession)执行两条相同的SQL在未指定Cache时Mybatis将查询两次数据库在指定了Cache时Mybatis只查询了一次数据库第二次是从缓存中拿的。 
Cache Hit Ratio 表示缓存命中率。  开启二级缓存后每执行一次查询系统都会计算一次二级缓存的命中率。  第一次查询也是先从缓存中查询只不过缓存中一定是没有的。  所以会再从DB中查询。由于二级缓存中不存在该数据所以命中率为0.但第二次查询是从二级缓存中读取的所以这一次的命中率为1/20.5。  当然若有第三次查询则命中率为1/30.66  0.5这个值可以从上面开启cache的图看出来0.0的值未截取到~漏掉了~ 
注意:  增删改操作无论是否进行提交sqlSession.commit()均会清空一级、二级缓存使查询再次从DB中select。  说明  二级缓存的清空实质上是对所查找key对应的value置为null而非将 
二级缓存的使用原则 只能在一个命名空间下使用二级缓存  由于二级缓存中的数据是基于namespace的即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作那么这多个namespace中的数据可能就会出现不一致现象。  在单表上使用二级缓存  如果一个表与其它表有关联关系那么久非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰所以就有可能出现多个namespace中的数据不一致现象。  查询多于修改时使用二级缓存  在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存对二级缓存的频繁刷新将降低系统性能。