查看“进阶:查询缓存”的源代码
←
进阶:查询缓存
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:Mybatis]] __TOC__ == 关于 == [[File:Mybatis缓存.png|800px]] Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。<br/> Mybatis默认开启一级缓存。 <br/><br/> Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。<br/> Mybatis二级缓存默认关闭。 == 一级缓存 == [[File:Mybatis一级缓存.png|500px]] # 一级缓存区域是根据SqlSession为单位划分的。 # 每次查询会先从缓存区域找,如果找不到再从数据库查询,并将查询到的数据写入缓存。 # Mybatis内部存储缓存使用一个'''HashMap'''。 #* key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象; # sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。 === 示例 === <syntaxhighlight lang="java"> @Test public void testCache1() throws Exception{ SqlSession sqlSession = sqlSessionFactory.openSession();//创建代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //以下查询使用一个SqlSession //第一次发起请求,查询id为1的用户 User user1 = userMapper.findUserById(1); System.out.println(user1); //第二次发起请求,查询id为1的用户 //与user1执行的是相同sql,可以使用一级缓存 User user2 = userMapper.findUserById(1); System.out.println(user2); //在同一个session执行更新 //执行commit操作,清空一级缓存,避免脏读。 User user_update = new User(); user_update.setId(1); user_update.setUsername("李奎"); userMapper.updateUser(user_update); session.commit(); //再次发起请求,查询id为1的用户 //缓存无相应内容,需要查询数据库 User user3 = userMapper.findUserById(1); System.out.println(user3); //关闭sqlSession sqlSession.close(); } </syntaxhighlight> == 二级缓存 == [[File:Mybatis二级缓存.png|500px]] # '''二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域'''。 #* 如果使用mapper代理方法每个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper划分; # 每次查询会先从缓存区域找,如果找不到再从数据库查询,并将查询到的数据写入缓存。 # Mybatis内部存储缓存使用一个'''HashMap'''。 #* key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象; # sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。 === 设置 === ==== SqlMapConfig.xml ==== {| class="wikitable" ! 设置 !! 描述 !! 允许值 !! 默认值 |- | cacheEnabled | 对在此配置文件下的所有cache 进行全局性开/关设置。 | <nowiki>true | false</nowiki> | true |} Mybatis的二级缓存默认是不开启的,需要时在SqlMapConfig.xml中加入: <syntaxhighlight lang="xml"> <setting name="cacheEnabled" value="true"/> </syntaxhighlight> ==== Mapper.xml ==== 开启二级缓存不光需要进行全局设置,'''同时必须在需要的mapper.xml映射文件中启用二级缓存''': <syntaxhighlight lang="xml"> <cache /> </syntaxhighlight> <syntaxhighlight lang="xml"> <cache type="" /> </syntaxhighlight> * '''type''':指定cache接口的实现类的类型 *# 指定cache接口的实现类的类型 *# Mybatis和ehcache整合,需要配置type为ehcache实现cache接口的类型 如: <syntaxhighlight lang="xml"> <mapper namespace="cn.itcast.mybatis.mapper.UserMapper"> <!-- 开启本mapper的namespace下的二缓存 --> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/> ... </syntaxhighlight> ===== Cache参数 ===== {| class="wikitable" ! 设置 !! 描述 !! 允许值 !! 默认值 |- | flushInterval | 缓存刷新间隔(单位毫秒)。 * 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。 | 任意正整数 | Not set |- | size | 设置缓存结果对象或列表的引用大小。 | 任意正整数 | 1024 |- | readOnly | 设置缓存只读。只读缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。 # 在不同线程中的调用者之间修改它们会导致冲突; # 可读写的缓存会返回缓存对象的拷贝(通过序列化),慢,安全(因此该属性默认false); | <nowiki>true | false</nowiki> | false |- | eviction | 缓存收回策略。 | # LRU:最近最少使用的策略,移除最长时间不被使用的对象 # FIFO:先进先出测试,按对象进行缓存的顺序来移除他们 # SOFT:软引用策略,移除基于垃圾回收器状态和软引用规则的对象 # WEAK:弱引用策略,更积极的移除基于垃圾收集器状态和弱引用规则的对象 | LRU |} 如: <syntaxhighlight lang="xml"> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> </syntaxhighlight> : 使用了一个“以FIFO进行回收,并每隔 60 秒刷新,缓存Hashmap有512个引用,而且返回只读对象”的二级缓存。 ==== statement ==== ===== 禁用缓存 ===== 在statement中设置useCache=false可以禁用当前statement(即当前select语句)的二级缓存,即每次查询都会发出sql去查询。 # 默认情况是true,即该sql使用二级缓存; # 针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存; 如: <syntaxhighlight lang="xml"> <select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false"> </syntaxhighlight> ===== 刷新缓存 ===== 在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。 # 默认情况下为true(即刷新缓存),如果改成false则不会刷新。 # 一般下执行完commit操作都需要刷新缓存,以避免数据库脏读。 # (使用缓存时,如果手动修改数据库表中的查询数据会出现脏读)。 如: <syntaxhighlight lang="xml"> <insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true"> </syntaxhighlight> ==== po ==== 二级缓存需要'''查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作''' * 注意如果存在父类、成员pojo都需要实现序列化接口。 <syntaxhighlight lang="java"> public class Orders implements Serializable public class User implements Serializable ... </syntaxhighlight> 如: <syntaxhighlight lang="java" highlight="1"> public class User implements Serializable { //属性名和数据库表的字段对应 private int id; private String username;// 用户姓名 private String sex;// 性别 private Date birthday;// 生日 private String address;// 地址 //用户创建的订单列表 private List<Orders> ordersList; </syntaxhighlight> === 示例 === <syntaxhighlight lang="java"> @Test public void testCache2() throws Exception { SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); SqlSession sqlSession3 = sqlSessionFactory.openSession(); SqlSession sqlSession4 = sqlSessionFactory.openSession(); // 创建代理对象 UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); // 第一次发起请求,查询id为1的用户 User user1 = userMapper1.findUserById(1); System.out.println(user1); //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域 sqlSession1.close(); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); // 第二次发起请求,查询id为1的用户 // 与sqlSession2请求相同,读取二级缓存,而不查询数据库 User user2 = userMapper2.findUserById(1); System.out.println(user2); //这里关闭操作,并将sqlsession中的数据写到二级缓存区域 sqlSession2.close(); //使用sqlSession3执行commit()操作 UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class); User user = userMapper3.findUserById(1); user.setUsername("张明明"); userMapper3.updateUser(user); //执行提交,清空UserMapper下边的二级缓存 sqlSession3.commit(); sqlSession3.close(); UserMapper userMapper4 = sqlSession4.getMapper(UserMapper.class); // 再一次发起请求,查询id为1的用户, // 由于commit操作导致缓存被清空,这里重新发出sql操作 User user4 = userMapper4.findUserById(1); System.out.println(user4); //这里关闭操作,并将sqlsession中的数据写到二级缓存区域 sqlSession4.close(); } </syntaxhighlight> == 整合ehcache == EhCache 是一个纯Java的进程内缓存框架,是一种广泛使用的开源Java分布式缓存,具有快速、精干等特点,是Hibernate中默认的CacheProvider。<br/> 而 Mybatis 的特长是sql操作,而非缓存数据的管理,为了提高缓存的性能将mybatis和第三方的缓存数据库整合,比如ehcache、memcache、redis等。 === 整合原理 === mybatis提供二级缓存Cache接口: : [[File:mybatis二级缓存接口Cache.png|200px]] 它的默认实现类: : [[File:mybatis二级缓存接口Cache的默认实现类.png|200px]] '''通过实现Cache接口,可以实现mybatis与其它缓存数据库整合。'''<br/> <br/> 如:<br/> : [[File:EhcacheCache是ehcache对Cache接口的实现.png|200px]]<br/> : EhcacheCache是ehcache对Cache接口的实现。 === 整合步骤 === ==== 依赖 ==== [[File:ehcache依赖包.jpg|200px]] 依赖包: # '''ehcache-core-2.6.5.jar''':ehcache核心包 # '''mybatis-ehcache-1.0.2.jar''':桥接包 Maven坐标: <syntaxhighlight lang="xml"> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.2</version> </dependency> </syntaxhighlight> ==== EhCache配置文件 ==== classpath下添加'''ehcache.xml''': <syntaxhighlight lang="xml"> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <diskStore path="D:\develop\ehcache" /> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache> </syntaxhighlight> {| class="wikitable" |+ 配置说明 ! 配置 !! 说明 |- | diskStore | 指定数据在磁盘中的存储位置 |- | defaultCache | 当借助<code>CacheManager.add("demoCache")</code>创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略 |} :{| class="wikitable" |+ defalutCache的“必须属性” ! 属性 !! 说明 !! 值 |- | maxElementsInMemory | 在内存中缓存的element的最大数目 | |- | maxElementsOnDisk | 在磁盘上缓存的element的最大数目,若是0表示无穷大 | |- | eternal | 设定缓存的elements是否永远不过期。 | # 如果为true,则缓存的数据始终有效; # 如果为false,根据timeToIdleSeconds,timeToLiveSeconds判断过期 |- | overflowToDisk | 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 | |} :{| class="wikitable" |+ defalutCache的“可选属性” ! 属性 !! 说明 !! 值 |- | timeToIdleSeconds | 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除 | 默认值是0,也就是可闲置时间无穷大 |- | timeToLiveSeconds | 缓存element的有效生命期 | 默认是0,也就是element存活时间无穷大 |- | diskSpoolBufferSizeMB | 设置DiskStore(磁盘缓存)的缓存区大小 | 默认是30MB,每个Cache都应该有自己的一个缓冲区 |- | diskPersistent | 在VM重启的时候是否启用磁盘保存EhCache中的数据 | 默认是false |- | diskExpiryThreadIntervalSeconds | 磁盘缓存的清理线程运行间隔 | 默认是120秒,即每隔120s,相应的线程会进行一次EhCache中数据的清理工作 |- | memoryStoreEvictionPolicy | 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略 | # LRU:最近最少使用(默认) # LFU:最不常使用 # FIFO:先进先出 |} ==== 开启EhCache ==== 修改mapper.xml文件,在cache中指定EhcacheCache: <syntaxhighlight lang="xml"> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/> </syntaxhighlight> 根据需求调整缓存参数: <syntaxhighlight lang="xml"> <cache type="org.mybatis.caches.ehcache.EhcacheCache" > <property name="timeToIdleSeconds" value="3600"/> <property name="timeToLiveSeconds" value="3600"/> <!-- 同ehcache参数maxElementsInMemory --> <property name="maxEntriesLocalHeap" value="1000"/> <!-- 同ehcache参数maxElementsOnDisk --> <property name="maxEntriesLocalDisk" value="10000000"/> <property name="memoryStoreEvictionPolicy" value="LRU"/> </cache> </syntaxhighlight> === 应用场景 === 对于访问多的查询请求且用户对查询结果'''实时性要求不高''',此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。 实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
返回至“
进阶:查询缓存
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息