1.什么是缓存113?
缓存:cache
1.1缓存的作⽤:113通过减少IO⽅式,来提⾼程序的执⾏效率。
1.2mybatis缓存:113将select语句的查询结果放入缓存(内存)中⼀第二个是这个select语句,直接从缓存中取出,不再检查数据库。⼀⽅⾯IO减少了。另一个⼀⽅⾯不再执⾏繁琐的搜索算法。效率高⼤⼤提升。
1.3mybatis缓存包括:114⼀级缓存:将查询到的数据存储在SqlSesion中。
⼆级缓存:将查询到的数据存储在SqlSessionFactory中。
或者集成其他第三个⽅的缓存:⽐如Ehcache[Java语言⾔开发的】、Memcache【C语⾔开发的】
等。
缓存仅针对DQL语句,即缓存机制仅对应select语句。
2.一级缓存113⼀默认情况下,级缓存是开放的。不需要任何配置。
原理:只要使用⽤同⼀SqlSession对象执行⾏同⼀条SQL语句,就会⾛缓存。
<select id="selectById" resultType="Car"> select * from t_car where id = #{id} </select>
@Test public void testSelectById(){ SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper1 = sqlSession.getMapper(CarMapper.class); Car car1 = mapper1.selectById(43L); System.out.println(car1); CarMapper mapper2 = sqlSession.getMapper(CarMapper.class); Car car2 = mapper2.selectById(43L); System.out.println(car2); //sqlSession.commit(); sqlSession.close(); }
2.1什么情况下不行?⾛缓存?1152.1.1第⼀种类:不同的SqlSession对象。115
///不要走缓存 // 如需获取不同的SqlSession对象,则不能使用以下代码。 115 @Test public void testSelectById() throws Exception{ //SqlSession sqlSession = SqlSessionUtil.openSession(); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); SqlSession sqlsession = sqlSessionFactory.openSession(); SqlSession sqlsession2sion = sqlSessionFactory.openSession(); CarMapper mapper1 = sqlsession.getMapper(CarMapper.class); Car car1 = mapper1.selectById(43L); System.out.println(car1); CarMapper mapper2 = sqlsession2sion.getMapper(CarMapper.class); Car car2 = mapper2.selectById(43L); System.out.println(car2); sqlsession.close(); sqlsession2sion.close(); }
2.1.2第⼆种类:查询条件发生变化。1162.1.3⼀有两种类型的缓存失效:116
你在第一次DQL和第二次DQL之间做了以下两件事中的任何一件,都会清空一级缓存:
1.实施了sqlsession的clearcache()方法,即手动清空缓存。
2.执行INSERT或DELETE或UPDATE语句。无论操作哪个表,都会清空一级缓存。
第⼀种:第⼀次查询和第⼆二次查询之间,⼿动清空了⼀级缓存。
@Test public void testSelectById(){ SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper1 = sqlSession.getMapper(CarMapper.class); Car car1 = mapper1.selectById(43L); System.out.println(car1); // 手动清空一级缓存 116A sqlSession.clearCache(); CarMapper mapper2 = sqlSession.getMapper(CarMapper.class); Car car2 = mapper2.selectById(43L); System.out.println(car2); //sqlSession.commit(); sqlSession.close(); }
第⼆种:第⼀次查询和第⼆二次查询之间,执行⾏增加或删除操作。[这个增加或删除与哪个表格无关,只要有insertdeleteupdate操作,⼀等级缓存失效。
@Test public void testSelectById(){ SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper1 = sqlSession.getMapper(CarMapper.class); Car car1 = mapper1.selectById(43L); System.out.println(car1); // 手动清空一级缓存 116A //sqlSession.clearCache(); // INSERT在这里实施 DELETE UPDATE中的任何句子。而且与表无关。116 CarMapper mapper = sqlSession.getMapper(CarMapper.class); mapper.insertClazz(2000, “高三三班”; CarMapper mapper2 = sqlSession.getMapper(CarMapper.class); Car car2 = mapper2.selectById(43L); System.out.println(car2); sqlSession.commit(); sqlSession.close(); }
3.二级缓存117
⼆SqlSessionFactory是级缓存的范围。
3.1使⽤⼆等级缓存需要具备以下几点⼏个条件:1171.全局打开或关闭所有映射器配置⽂已配置在件中的任何缓存。默认为true,⽆需设置。
2.在需要使⽤⼆Sqlmaper级缓存.xml⽂零件中添加配置:
3.使⽤⼆级缓存的物理对象必须是可序列化的,即java必须实现.io.Serializable⼝
4.SqlSession对象关闭或提交后,⼀级缓存中的数据将被写入⼊到⼆在等级缓存中。此时⼆级缓存才可⽤。
<!--二次缓存试验 117--> <select id=“selectbyId2” resultType="Car"> select * from t_car where id = #{id} </select>
//测试二级缓存 117 @Test public void testselectbyid2() throws Exception{ // 这里只有一个SqlSessionFactory对象。SqlSessionFactory对应于二级缓存。 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); SqlSession sqlsession = sqlSessionFactory.openSession(); SqlSession sqlsession2sion = sqlSessionFactory.openSession(); CarMapper mapper1 = sqlsession.getMapper(CarMapper.class); CarMapper mapper2 = sqlsession2sion.getMapper(CarMapper.class); // 本行代码执行完成后,事实上,数据是缓存到一级缓存。(sqlsession1是一级缓存。(sqlsession1是一级缓存。) Car car1 = mapper1.selectByID2(43L); System.out.println(car1); // 如果SqlSession1对象在这里不关闭,二次缓存中仍然没有数据。 // 如果执行此行代码,sqlsession1的一级缓存中的数据将放入二级缓存中。 sqlsession.close(); // 代码执行完成后,数据实际上会缓存到一级缓存中。(sqlsession2是一级缓存。) Car car2 = mapper2.selectByID2(43L); System.out.println(car2); // 在此执行程序时,sqlsession1的一级缓存中的数据将被写入二级缓存中。 //sqlSesion.close(); // 在此执行程序时,sqlsession2的一级缓存数据将写入二级缓存。 sqlsession2sion.close(); }
3.2⼆级缓存失效:118
只要增删操作发生在两次查询之间。⼆等级缓存会失效。【⼀等级缓存也会失效]
3.3⼆级缓存相关配置:1181.eviction:指定从缓存中删除对象的淘汰算法。默认采集⽤LRU策略。
a.LRU:LeastRecentlyUsed。最近最少使⽤。优先在间隔时间内淘汰⽤频率最低的对象。(其实也有。⼀淘汰算法LFU最不常见⽤。)
b.FIFO:FirstInFirstOut。⼀先进先出的数据缓存器。先进⼊⼆级缓存的对象首先被淘汰。
c.SOFT:软引⽤。淘汰软引⽤指向对象。具体算法与JVM垃圾回收算法有关。
d.WEAK:弱引⽤。淘汰弱引⽤指向对象。具体算法与JVM垃圾回收算法有关。
2.flushInterval:
a.⼆等级缓存的刷新时间间隔。单位毫秒。如果没有设置。这意味着缓存没有刷新,只要内存⾜够⼤,⼀直会向⼆缓存数据在等级缓存中。去除⾮执⾏了增删改。
3.readOnly:
a.true:多个相同的sql语句执行⾏返回的对象是共享的⼀性能好。但多线程并发可能存在安全问题。
b.false:多个相同的sql语句执行⾏之后返回的对象是副本,调整⽤clone⽅法。性能⼀一般。但是安全。
4.size:
a.设置⼆java对象最多可存储在等级缓存中。默认值1024。
4.集成Ehcache(集成第三方缓存机制)119集成Ehcache取代mybatiss⾃带的⼆级缓存。⼀级缓存是⽆法替代的。
mybatis提供外部接收⼝,也可以集成第三⽅缓存组件。⽐比如EhCache、Memcache等。都可以。Ehcache是Java写的。Memcache是C语⾔所以mybatis集成EhCache比较常见⻅,集成可以按照以下步骤完成:
第⼀步:引⼊整合ehcache对mybatis的依赖。
pom.xml
<!--集成ehcache的mybatis组件--> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.2</version> </dependency>
第⼆步骤:在类的根路径下新建echcache.xml⽂并提供以下配置信息。
echcache.xml
<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!--磁盘存储:将暂时不使用缓存的对象转移到类似Windows系统的硬盘上的虚拟内存--> <diskStore path="e:/ehcache"/> <!--磁盘存储:将暂时不使用缓存的对象转移到类似Windows系统的硬盘上的虚拟内存--> <diskStore path="e:/ehcache"/> <!--defaultCache:默认管理策略--> <!--eternal:elements设置缓存是否永远不会过期。如果是true,缓存的数据总是有效的。如果是false,则根据timetoidleseconds和timetoliveseconds判断--> <!--maxElementsInMemory:element在内存中缓存的最大数量--> <!--overflowToDisk:如果内存中的数据超过内存限制,是否应该缓存到磁盘上--> <!--diskPersistent:磁盘上是否持久。数据重启jvm后是否有效。默认为false--> <!--timeToIdleSeconds:对象的空闲时间(单位:秒)是指对象在没有被访问的情况下失败的时间。只有eternal对false有效。默认值0表示可以一直访问--> <!--timeToLiveSeconds:对象存活时间(单位:秒)是指对象从创建到失效所需的时间。只有eternal对false有效。默认值0表示可以一直访问--> <!--memoryStoreEvictionPolicy:缓存的3 种清空策略--> <!--FIFO:first in first out (先进先出)--> <!--LFU:Less Frequently Used (至少使用).这意味着它一直至少被使用。缓存元素有hittt 属性,hit 最小值将被清除缓存--> <!--LRU:Least Recently Used(最近最少使用). (ehcache 默认值).缓存元素有时间戳,当缓存容量满了,又需要腾出空间来缓存新元素时,现有缓存元素中时间戳离当前时间最远的元素将被清除--> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/></ehcache>
第三步:修改Sqlmaper.xml⽂添加type属性的件中标签。
CarMapper.xml
<!--集成Ehcache组件--> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
第四步:编写测试程序⽤。
//测试二级缓存 117 @Test public void testselectbyid2() throws Exception{ // 这里只有一个SqlSessionFactory对象。SqlSessionFactory对应于二级缓存。 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); SqlSession sqlsession = sqlSessionFactory.openSession(); SqlSession sqlsession2sion = sqlSessionFactory.openSession(); CarMapper mapper1 = sqlsession.getMapper(CarMapper.class); CarMapper mapper2 = sqlsession2sion.getMapper(CarMapper.class); // 本行代码执行完成后,事实上,数据是缓存到一级缓存。(sqlsession1是一级缓存。(sqlsession1是一级缓存。) Car car1 = mapper1.selectByID2(43L); System.out.println(car1); // 如果SqlSession1对象在这里不关闭,二次缓存中仍然没有数据。 // 如果执行此行代码,sqlsession1的一级缓存中的数据将放入二级缓存中。 sqlsession.close(); // 代码执行完成后,数据实际上会缓存到一级缓存中。(sqlsession2是一级缓存。) Car car2 = mapper2.selectByID2(43L); System.out.println(car2); // 在此执行程序时,sqlsession1的一级缓存中的数据将被写入二级缓存中。 //sqlSesion.close(); // 在此执行程序时,sqlsession2的一级缓存数据将写入二级缓存。 sqlsession2sion.close(); }
5.代码汇总main中com.powernode.mybatis.mapperCarMapper
package com.powernode.mybatis.mapper;import com.powernode.mybatis.pojo.Car;import org.apache.ibatis.annotations.Param;//演示缓存机制 115public interface CarMapper { /** * 二次缓存试验 117 * @param id * @return */ Car selectById2(Long id); /** * 保存班级信息 116 * @param cid * @param cname * @return */ int insertClazz(@Param("cid") Integer cid, @Param("cname") String cname); /** * 根据id获取Car信息。115 * @param id * @return */ Car selectById(Long id);}
CarMapper.xml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.powernode.mybatis.mapper.CarMapper"> <!-- 二级缓存机制在默认情况下是开放的。-- 二级缓存机制在默认情况下是开放的。 只需在相应的Sqlmaper中使用.以下标签添加到xml文件中。用来表示“我”使用二次缓存。 --> <!-- <cache/>--> <!--集成Ehcache组件 119--> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/> <insert id="insertClazz"> insert into t_clazz values(#{cid},#{cname}) </insert> <!--测试一级缓存 116--> <select id="selectById" resultType="Car"> select * from t_car where id = #{id} </select> <!--二次缓存试验 117--> <select id=“selectbyId2” resultType="Car"> select * from t_car where id = #{id} </select></mapper>
test中com.powernode.mybatis.testCarMapperTest
package com.powernode.mybatis.test;import com.powernode.mybatis.mapper.CarMapper;import com.powernode.mybatis.pojo.Car;import com.powernode.mybatis.utils.SqlSessionUtil;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.junit.Test;//研究缓存机制 115public class CarMapperTest { //测试二级缓存 117 @Test public void testselectbyid2() throws Exception{ // 这里只有一个SqlSessionFactory对象。SqlSessionFactory对应于二级缓存。 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); SqlSession sqlsession = sqlSessionFactory.openSession(); SqlSession sqlsession2sion = sqlSessionFactory.openSession(); CarMapper mapper1 = sqlsession.getMapper(CarMapper.class); CarMapper mapper2 = sqlsession2sion.getMapper(CarMapper.class); // 本行代码执行完成后,事实上,数据是缓存到一级缓存。(sqlsession1是一级缓存。(sqlsession1是一级缓存。) Car car1 = mapper1.selectByID2(43L); System.out.println(car1); // 如果SqlSession1对象在这里不关闭,二次缓存中仍然没有数据。 // 如果执行此行代码,sqlsession1的一级缓存中的数据将放入二级缓存中。 sqlsession.close(); // 代码执行完成后,数据实际上会缓存到一级缓存中。(sqlsession2是一级缓存。) Car car2 = mapper2.selectByID2(43L); System.out.println(car2); // 在此执行程序时,sqlsession1的一级缓存中的数据将被写入二级缓存中。 //sqlSesion.close(); // 在此执行程序时,sqlsession2的一级缓存数据将写入二级缓存。 sqlsession2sion.close(); } ///不要走缓存 // 如需获取不同的SqlSession对象,则不能使用以下代码。 115 /* @Test public void testSelectById() throws Exception{ //SqlSession sqlSession = SqlSessionUtil.openSession(); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml")); SqlSession sqlsession = sqlSessionFactory.openSession(); SqlSession sqlsession2sion = sqlSessionFactory.openSession(); CarMapper mapper1 = sqlsession.getMapper(CarMapper.class); Car car1 = mapper1.selectById(43L); System.out.println(car1); CarMapper mapper2 = sqlsession2sion.getMapper(CarMapper.class); Car car2 = mapper2.selectById(43L); System.out.println(car2); sqlsession.close(); sqlsession2sion.close(); }*/ // 思考:什么时候不走缓存? // SqlSession对象不一样,肯定不会缓存。 // SqlSession对象不一样,肯定不会缓存。 // 查询条件不同,肯定不要缓存。 // 思考:什么时候一级缓存失效? // 你在第一次DQL和第二次DQL之间做了以下两件事中的任何一件,都会清空一级缓存: // 1. 实施了sqlsessionclearcache()方法,即手动清空缓存。 // 2. 执行INSERT或DELETE或UPDATE语句。无论操作哪张表,都会清空一级缓存。 ///根据id获取Car信息。115 @Test public void testSelectById(){ SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper1 = sqlSession.getMapper(CarMapper.class); Car car1 = mapper1.selectById(43L); System.out.println(car1); // 手动清空一级缓存 116A //sqlSession.clearCache(); // INSERT在这里实施 DELETE UPDATE中的任何句子。而且与表无关。116 /*CarMapper mapper = sqlSession.getMapper(CarMapper.class); mapper.insertClazz(2000, “高三三班”;*/ CarMapper mapper2 = sqlSession.getMapper(CarMapper.class); Car car2 = mapper2.selectById(43L); System.out.println(car2); sqlSession.commit(); sqlSession.close(); }}
com.powernode.mybatis.pojoCar
package com.powernode.mybatis.pojo;import java.io.Serializable;/** * Pojo类包装汽车相关信息。普通java类。 * @author 动力节点 * @version 1.0 * @since 1.0 */public class Car implements Serializable { // 数据库表中的字段应与pojo属性一一对应。 // 建议使用包装类,以防止null问题。 private Long id; private String carNum; private String brand; private Double guidePrice; private String produceTime; private String carType; @Override public String toString() { return "Car{" + "id=" + id + ", carNum='" + carNum + '\'' + ", brand='" + brand + '\'' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + '\'' + ", carType='" + carType + '\'' + '}'; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getCarNum() { return carNum; } /*public String getXyz() { return carNum; }*/ public void setCarNum(String carNum) { this.carNum = carNum; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public Double getGuidePrice() { return guidePrice; } public void setGuidePrice(Double guidePrice) { this.guidePrice = guidePrice; } public String getProduceTime() { return produceTime; } public void setProduceTime(String produceTime) { this.produceTime = produceTime; } public String getCarType() { return carType; } public void setCarType(String carType) { this.carType = carType; } public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) { this.id = id; this.carNum = carNum; this.brand = brand; this.guidePrice = guidePrice; this.produceTime = produceTime; this.carType = carType; } public Car() { }}
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <properties resource="jdbc.properties"/> <!--mapunderscoretocamelcase开始驼峰命名自动映射--> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <package name="com.powernode.mybatis.pojo"/> </typeAliases> <environments default="dev"> <environment id="dev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <package name="com.powernode.mybatis.mapper"/> </mappers></configuration>
echcache.xml
<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!--磁盘存储:将暂时不使用缓存的对象转移到类似Windows系统的硬盘上的虚拟内存--> <diskStore path="e:/ehcache"/> <!--磁盘存储:将暂时不使用缓存的对象转移到类似Windows系统的硬盘上的虚拟内存--> <diskStore path="e:/ehcache"/> <!--defaultCache:默认管理策略--> <!--eternal:elements设置缓存是否永远不会过期。如果是true,缓存的数据总是有效的。如果是false,则根据timetoidleseconds和timetoliveseconds判断--> <!--maxElementsInMemory:element在内存中缓存的最大数量--> <!--overflowToDisk:如果内存中的数据超过内存限制,是否应该缓存到磁盘上--> <!--diskPersistent:磁盘上是否持久。数据重启jvm后是否有效。默认为false--> <!--timeToIdleSeconds:对象的空闲时间(单位:秒)是指对象在没有被访问的情况下失败的时间。只有eternal对false有效。默认值0表示可以一直访问--> <!--timeToLiveSeconds:对象存活时间(单位:秒)是指对象从创建到失效所需的时间。只有eternal对false有效。默认值0表示可以一直访问--> <!--memoryStoreEvictionPolicy:缓存的3 种清空策略--> <!--FIFO:first in first out (先进先出)--> <!--LFU:Less Frequently Used (至少使用).这意味着它一直至少被使用。缓存元素有hittt 属性,hit 最小值将被清除缓存--> <!--LRU:Least Recently Used(最近最少使用). (ehcache 默认值).缓存元素有时间戳,当缓存容量满了,又需要腾出空间来缓存新元素时,现有缓存元素中时间戳离当前时间最远的元素将被清除--> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/></ehcache>
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.powernode</groupId> <artifactId>course22</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <!--集成ehcache的mybatis组件--> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> </dependency> </dependencies> <properties> <!-- jdk版本用于编译代码--> <maven.compiler.source>1.8</maven.compiler.source> <!-- 使用jdk版本的操作程序--> <maven.compiler.target>1.8</maven.compiler.target> </properties></project>
剩余的
utils
logback.xml
jdbc.properties
不做赘述