当前位置: 首页 > 后端技术 > Java

MyBatis的一级缓存和二级缓存

时间:2023-04-01 14:48:21 Java

一、简介MyBatis的一级缓存是基于数据库session(SqlSession对象)的,默认开启。二级缓存基于全局(nameSpace),需要配置开启。2、MyBatis的主要层次结构使用MyBatis的代码对数据库进行操作,可以看到的是SqlSession对象。其实这只是MyBatis暴露出来的接口。MyBatis的核心组件有以下接口和类:1.SqlSession:MyBatis工作的主要顶层API,代表一个与数据库交互的会话,完成必要的数据库增删改查功能.2?、Executor:MyBatis执行器,整个MyBatis调度的核心,负责SQL语句的生成和查询缓存的维护。3?、StatementHandler:封装了JDBCStatement操作,负责JDBC语句的操作,比如设置参数,将Statement结果集转换成List集合等。4?、ParameterHandler:负责将用户传递的参数转换成JDBCStatement需要的参数。5?、ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换为List类型集合。6?、TypeHandler:负责Java数据类型和jdbc数据类型之间的映射和转换。7?、MappedStatement:MappedStatement维护了一个节点的封装。8?、SqlSource:负责根据用户传递的parameterObject动态生成SQL语句,将信息封装到BoundSql对象中,并返回。9?、BoundSql:表示动态生成的SQL语句和对应的参数信息。10、Configuration:MyBatis的所有配置信息都是在Configuration对象中维护的。上面这一堆接口和类之间的层次关系如图所示:MyBatis对外暴露的接口是SqlSession,最重要的是Executor接口。Executor的实现类BaseExecutor有一个Cache接口实现类PerpetualCache:PerpetualCache有一个HashMap属性总结:MyBatis封装了JDBC操作,暴露了SqlSession接口用于数据库操作。但其实MyBatis的核心接口是Executor,它负责SQL语句的生成和查询缓存的维护。如果没有缓存,就去查数据库,如果有缓存,就使用PerpetualCache中HashMap保存的数据缓存。MyBatis的一级缓存其实是存储在一个HashMap中的。HashMap如何判断查询方式是否相同?其实主要是通过HashMapBaseExecutor的key值:publicCacheKeycreateCacheKey(MappedStatementms,ObjectparameterObject,RowBoundsrowBounds,BoundSqlboundSql){if(this.closed){thrownewExecutorException("Executorwasclosed.");}else{CacheKeycacheKey=newCacheKey();cacheKey.update(ms.getId());cacheKey.update(rowBounds.getOffset());cacheKey.update(rowBounds.getLimit());cacheKey.update(boundSql.getSql());ListparameterMappings=boundSql.getParameterMappings();TypeHandlerRegistrytypeHandlerRegistry=ms.getConfiguration().getTypeHandlerRegistry();迭代器var8=parameterMappings.iterator();while(var8.hasNext()){ParameterMappingparameterMapping=(ParameterMapping)var8.next();if(parameterMapping.getMode()!=ParameterMode.OUT){StringpropertyName=parameterMapping.getProperty();对象值;如果(boundSql.hasAdditionalParameter(propertyName)){value=boundSql.getAdditionalParameter(propertyName);}elseif(parameterObject==null){value=null;}elseif(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){value=parameterObject;}else{MetaObjectmetaObject=this.configuration.newMetaObject(parameterObject);value=metaObject.getValue(propertyName);}cacheKey.update(value);}}if(this.configuration.getEnvironment()!=null){cacheKey.update(this.configuration.getEnvironment().getId());}返回缓存键;}}从代码中可以看出,如果以下条件相同,则可以判断两次查询相同:1?、statementId2?、RowBoundsOffset、限制结果集分页属性3?、SQL语句4?、参数值传递给JDBC3、MyBatis一级缓存1?、MyBatis最简单的一级缓存组织形式是在一个session表示中创建的——一个SqlSession对象一个本地缓存(localcache),对于每一个查询,它都会尝试查找是否根据查询条件在本地缓存中,如果在,则直接从缓存中取出,返回给用户;否则,从数据库中读取数据,将查询结果存入缓存并返回给用户,与原来的保存方式类似,只是将一个简单的对象换成了更复杂的封装的LocalCache对象。其实SqlSession只是MyBatis的一个对外接口。SqlSession将自己的工作分配给了Executor的角色,Executor负责完成对数据库的各种操作。当一个SqlSession对象被创建时,MyBatis会为这个SqlSession对象创建一个新的Executor,缓存信息就维护在这个Executor中。MyBatis将缓存和缓存相关的操作封装在Cache接口中。它们之间的组织关系大致如下:2.一级缓存的生命周期MyBatis在开启一个数据库会话时会创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。新增了PerpetualCache对象(Cache接口的实现类);当会话结束时,SqlSession对象、它内部的Executor对象和PerpetualCache对象也被释放。如果SqlSession调用close(),一级缓存PerpetualCache对象会被释放,一级缓存不可用。如果SqlSession调用clearCache(),PerpetualCache对象中的数据会被清除,但SqlSession对象仍然可以使用。在SqlSession中执行任何增加/删除/修改操作后,执行事务提交commit(),PerpetualCache对象的数据将被清除,但SqlSession对象可以继续使用。4、MyBatis的二级缓存2、二级缓存的使用场景类似于统计排行榜的查询,可能会涉及到多个表中很多字段的查询统计和排序,非常耗时又费力。如果每次都去数据库查询并显示排行榜数据,那么到了排行榜就会卡很久,这种卡顿是用户无法忍受的。做一级缓存也不可行。每次一个SqlSession请求,是不是每个客户都要冻结一次?因此,这种查询必须做成全局缓存。当应用程序启动时,查询数据被缓存,然后每周刷新一次数据。由此,简单总结一下二级缓存的特点和使用场景:二级缓存作用于全局,对于一些比较耗性能且对时效性不敏感的查询可以使用二级缓存。注意,如果开启了二级缓存,查询顺序为二级缓存→一级缓存→数据库。MyBatis二级缓存的配置必须配置为使用MyBatis中的二级缓存。正常使用二级缓存必须按照以下步骤:在全局设置中启用二级缓存......enabletaginxxxMapper.xml可以简写为:表示在xxxMapper.xml中启用了二级缓存,因为标签的每个属性都有一个默认值。缓存标签属性:eviction:缓存回收策略,该属性有如下值LRU——最近最少使用。移除最长时间未使用的对象。FIFO——先进先出。按照对象进入缓存的顺序移除对象。SOFT-软引用。根据垃圾收集器状态和软引用规则删除对象。弱-弱参考。基于垃圾收集器状态和弱引用规则更积极地移除对象。默认是LRUflushInterval:刷新间隔,可以设置为任意正整数,它们代表一个合理的时间周期,单位为毫秒。默认不设置,即没有刷新间隔,只有调用语句时才刷新缓存。size:引用的数量,可以设置为任意正整数,记住你缓存的对象数量和你运行环境中可用的内存资源数量。默认值为1024。readOnly:只读属性可以设置为true或false。只读缓存将缓存对象的相同实例返回给所有调用者。因此不能修改这些对象。这提供了显着的性能优势。读写缓存返回缓存对象的副本(通过序列化)。这速度较慢,但??安全,因此默认为false。相关实体类需要序列化,二级缓存中存放的JavaBean需要实现Serializable接口。序列化意味着将数据从内存传输到硬盘。反序列化意味着相反。MyBatis的二级缓存实际上是将数据放入硬盘文件中。如果要使用MyBatis的二级缓存,除了在需要缓存的mapper.xml中启用外,还需要目标实体类实现序列化接口。当实体类有父类或级联属性时,也必须实现序列化。useCache和flushCache的步骤不是必须的。这两个属性都属于查询标签。userCache用于设置是否关闭二级缓存。在语句中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发送sql去查询。默认情况下为真,即sql使用二级缓存。flushCache属性,默认为true,即刷新缓存,改为false则不刷新。如果在使用缓存时手动修改数据库表中的查询数据,就会出现脏读。5.Mybatis1?中涉及的设计模式,Builder模式,如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder2?,工厂模式,如SqlSessionFactory、ObjectFactory、MapperProxyFactory3?,单例模式,如ErrorContext和LogFactory4?,代理模式,核心Mybatis实现,例如MapperProxy、ConnectionLogger,使用jdk动态代理;而executor.loader包使用cglib或者javassist实现懒加载的效果5?、组合方式,比如SqlNode和各个子类ChooseSqlNode等6?、模板方法方式,比如BaseExecutor和SimpleExecutor,BaseTypeHandler和所有子类比如IntegerTypeHandler7?,适配器模式,如Log的Mybatis接口及其对jdbc,log4j8?等各种日志框架的适配,装饰器模式,如Cache包中的cache.decorators子包中各装饰器的实现9?,iterator模式,如theiteratormodePropertyTokenizer6.总结选择后调用SqlSession.close(),会将其一级缓存中的数据放入二级缓存中。此时一级缓存不存在,因为SqlSession是关闭的。选择后调用SqlSession.commit(),会把一级缓存中的数据放到二级缓存中,并清空一级缓存。对SqlSession进行更新(插入、删除、更新)后,不要同时调用SqlSession.commit/SqlSession.close(),只会清除自身的一级缓存,二级缓存不会被影响。在对SqlSession进行一次更新(插入、删除、更新)后,执行SqlSession.commit(),不仅会清除自身的一级缓存(执行更新操作的结果),还会清除二级缓存(执行commit()的效果)。对SqlSession进行更新(插入、删除、更新)后,执行SqlSession.close()(不执行SqlSession.commit()),有两种情况。当autoCommit为false时,只会清除自己的一级缓存(执行更新操作的效果),对二级缓存没有影响。当autoCommit为true时,二级缓存会被清空。