五、Mybati缓存(一级缓存与二级缓存)
一、一级缓存
1.什么是一级缓存
一级缓存是SqlSession级别的缓存,是基于PerpetualCache的HashMap本地缓存。在操作数据库时需要构造sqlSession对象,在对象中有个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的SqlSession之间缓存数据区域 (HashMap)是互不影响的。
2.作用域
一级缓存的作用域是同一个SqlSession在同一个SqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据(前提是同一个session),将不再从数据库查询,从而提高查询效率。
(Mybatis默认开启一级缓存)
注意:
当 Session flush ,commit(执行插入、更新、删除),或 close 之后,该Session中的所有 Cache 就将清空。
代码实现(详细的代码会放在最后)
一级缓存的简单测试 |
//@Test public void firstGradeCache(){ //获取一个session会话 SqlSession session = getSession(); //获取一个执行sql语句的映射接口 A MybatisCache firstGradeCacheA = session.getMapper(MybatisCache.class); //获取一个执行sql语句的映射接口 B MybatisCache firstGradeCacheB = session.getMapper(MybatisCache.class);
//通过A和B执行同一条sql Student studentA = firstGradeCacheA.queryStudentById(1); Student studentB = firstGradeCacheB.queryStudentById(1); //通过log4j发现sql语句执行了一次 ,并且两个对象相同。说明是第一次查询存入到内存里面的对象 System.out.println(studentA==studentB);
} |
缓存数据更新机制
当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。 |
//@Test public void firstGradeCacheCommit(){ //获取一个session会话 SqlSession session = getSession(); //获取一个执行sql语句的映射接口 A MybatisCache firstGradeCacheA = session.getMapper(MybatisCache.class); //获取一个执行sql语句的映射接口 update MybatisCache update= session.getMapper(MybatisCache.class); //获取一个执行sql语句的映射接口 A MybatisCache firstGradeCacheB = session.getMapper(MybatisCache.class); //让A先查询并获取实体1 Student studentA = firstGradeCacheA.queryStudentById(1); //创建一个修改的数据 Student updateStudent = new Student(1,"wahaha",2); //然后在用更新的执行接口修改一下数据 update.updateStudent(updateStudent); //然后提交一下会话 session.commit(); //再查一下相同的用户 Student studentB = firstGradeCacheB.queryStudentById(1); //会发现sql语句执行了两次,而且返回的对象也不是同一个地址了 System.out.println(studentA==studentB);
} |
二、二级缓存
1.什么是二级缓存
二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache、Hazelcast等。
2. 作用域
二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,
不同的sqlSession多次执行相同namespace下的Sql语句第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
二级缓存是跨SqlSession的
重点:因为mybatis默认一级缓存一直存在(为了数据的安全性考虑),所以说只有等session操作完成,并且close,或commit该session后(建议每次查询都记得关闭会话,commit使用不当的话会将所有缓存清空),查到的值才会存入到缓存中,供其他session使用
3.启用二级缓存
1).在核心配置文件Mybatis.xml中配置<setting>标签
(默认是开启的但是必须配置cache标签才能使用所以可以不配置)
属性 | 描述 | 允许值 | 默认值 |
cacheEnable | 对此配置文件下的所有cache进行全局性开关设置 | true/false | true |
Mybatis.xml文件配置 | |||
<!-- 二级缓存的配置 --> <settings> <setting name="cacheEnabled" value="true"/> </settings>
|
2).在Mapper.xml中开启二级缓存,当该namespace下的 sql执行完成会存储到它的缓存区域
(在mapper下第一行配置)
mapper.xml 文件配置 |
<mapper namespace=""> <cache eviction="FIFO" flushInterval="10800000" size="512" readOnly="true" type="实现Cache接口的类"></cache> ......... </Mapper>
|
<cache>标签中的属性简介
1. eviction 回收策略 (默认为 LRU)
【默认】LRU——最近最少使用的:移除最长时间不被使用的对象
【新】LFU——最近一段时间使用次数最少的(部分版本不能使用)
FIFO——先进先出的:按对象进入缓存的顺序来移除他们
SOFT——软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK——弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
2. flushInterval (刷新间隔)
可以被设置为任意的正整数(60*60*1000这种形式是不允许的),而且它们代表一个合理的毫秒形式的时间段。
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
3.size (引用数目)
可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024.
4.readOnly (只读)
属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过发序列化)。这会慢一些,但是安全,因此默认是false。
5.type 自定义缓存的类全类名
自定义缓存需要实现cache接口,并且重写其中的方法,就可以使用第三方缓存了
代码实现
不使用提交,关闭和使用提交关闭 |
@Test public void secondGradeCacheNoCommitAndClose(){
//获取一个session工厂 SqlSessionFactory factory = getSqlSessionFactory(); //获取两个不同的会话 SqlSession sessionA = factory.openSession(); SqlSession sessionB = factory.openSession(); //执行接口 MybatisCache firstGradeCacheA = sessionA.getMapper(MybatisCache.class); MybatisCache firstGradeCacheB= sessionB.getMapper(MybatisCache.class);
//查询id为1的学生 Student studentA=firstGradeCacheA.queryStudentById(1); Student studentB=firstGradeCacheB.queryStudentById(1); //比较两个结果相同(如果是使用了序列化和反序列化则不相同) System.out.println(studentA==studentB);
//如果不提交不关闭则数据一直在一级缓存中,会发起两个sql语句 }
|
使用提交将数据存入二级缓存(注意如果是C/U/D操作后提交则会清空所有缓存) |
@Test public void secondGradeCacheByCommit(){
//获取一个session工厂 SqlSessionFactory factory = getSqlSessionFactory(); //获取两个不同的会话 SqlSession sessionA = factory.openSession(); SqlSession sessionB = factory.openSession(); //执行接口 MybatisCache firstGradeCacheA = sessionA.getMapper(MybatisCache.class); MybatisCache firstGradeCacheB= sessionB.getMapper(MybatisCache.class);
//查询id为1的学生 Student studentA=firstGradeCacheA.queryStudentById(1); //关闭A会话,将一级缓存清空,存入二级缓存 sessionA.commit(); Student studentB=firstGradeCacheB.queryStudentById(1); //比较两个结果相同(如果是使用了序列化和反序列化则不相同) System.out.println(studentA==studentB); //通过log4j显示的运行结果可以看出语句值查询了一次 }
|
使用关闭将数据存入二级缓存 |
@Test public void secondGradeCacheClose(){
//获取一个session工厂 SqlSessionFactory factory = getSqlSessionFactory(); //获取两个不同的会话 SqlSession sessionA = factory.openSession(); SqlSession sessionB = factory.openSession(); //执行接口 MybatisCache firstGradeCacheA = sessionA.getMapper(MybatisCache.class); MybatisCache firstGradeCacheB= sessionB.getMapper(MybatisCache.class);
//查询id为1的学生 Student studentA=firstGradeCacheA.queryStudentById(1); //关闭A会话,将一级缓存清空,存入二级缓存 sessionA.close(); Student studentB=firstGradeCacheB.queryStudentById(1); //比较两个结果相同(如果是使用了序列化和反序列化则不相同) System.out.println(studentA==studentB); //通过log4j显示的运行结果可以看出语句值查询了一次 } |
三、自定义缓存的实现
(下面是mybatis使用Redis数据库作为自定义缓存的实例)
自定义缓存必须实现Cache接口,并且重写其中的方法才能使用
自定义缓存的实现 |
package cn.et.fuqiang.cache.xml;
import java.io.IOException; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.ibatis.cache.Cache;
import redis.clients.jedis.Jedis;
public class RedisCache implements Cache { /** * 二级缓存是针对namespace的所以该id其实就是namespace */ private String id; /** * 实现的是第三方缓存 redis * 使用Jedis */ private Jedis jedis; /** * 定义构造器,当容器自动调用时会实例化并传入对应的值 * @param id */ public RedisCache(final String id){ this.id=id; init(); } /** * 初始化的连接方法 */ public void init(){ jedis = new Jedis("localhost",6379); } /** * 返回namespace */ public String getId() { return id; } /** * 将值存入redis中 * 方法中使用了自定义的序列化方法,使用set(byte[] key,byte[] value)存入 */ public void putObject(Object key, Object value) {
if(key==null || value==null){ return; } try { //将值存入 jedis.set(SerializationUtil.objectToByte(key), SerializationUtil.objectToByte(value)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 从redis中查找值 * 方法中使用了自定义的反序列化方法,使用get(byte[] key)将值去除 */ public Object getObject(Object key) { if(key!=null){ try { byte[] value = jedis.get(SerializationUtil.objectToByte(key)); if(value==null){ return null; } return SerializationUtil.byteToObject(value); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } /** * 用于从缓存中删除指定的键值 */ public Object removeObject(Object key) { if(key!=null){ try { Object value = getObject(key); jedis.del(SerializationUtil.objectToByte(key)); return value; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } /** * 清空所有缓存的方法 */ public void clear() { jedis.flushAll(); }
public int getSize() {
return 0; } /** * 直接返回 ReadWriteLock的子类ReentrantReadWriteLock()实体 */ public ReadWriteLock getReadWriteLock() { return new ReentrantReadWriteLock(); }
} |
工具类SerializationUtil的代码
SerializationUtil 类 |
package cn.et.fuqiang.cache.xml;
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream;
public class SerializationUtil { /** * 序列化 * @param object * @return * @throws IOException */ public static byte[] objectToByte(Object object) throws IOException{ /* * 使用一个byte数组输出流将数据以byte数组写入到内存中 使用out.toByteArray()将数组返回 */ ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream obj = new ObjectOutputStream(out); obj.writeObject(object); return out.toByteArray(); }
/** * 反序列化 * @param data * @return * @throws IOException * @throws ClassNotFoundException */ public static Object byteToObject(byte[] data) throws IOException, ClassNotFoundException{ /* * 使用一个byte数组读取流 ,将数组读入到ByteArrayInputStream流中最后反序列化出去 */ ByteArrayInputStream inputStream = new ByteArrayInputStream(data); ObjectInputStream obj = new ObjectInputStream(inputStream); return obj.readObject(); }
} |
总结:一级缓存和二级缓存独有缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有select 中的缓存将被clear。
以上代码实例所有代码如下
1.Mybatis主配置文件
mybatis.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> <!-- 指定jdbc的配置文件位置 --> <properties resource="cn/et/fuqiang/cache/jdbc.properties"></properties> <!-- 二级缓存的配置 --> <settings> <setting name="cacheEnabled" value="true"/> </settings>
<!-- 配置类别名 --> <typeAliases> <!-- 使用该标签将该包下的所有类统一起别名 默认为类名首字母小写 --> <package name="cn.et.fuqiang.cache.entity"/> </typeAliases> <!-- 配置jdbc环境 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <!-- 配置数据库连接信息 --> <dataSource type="POOLED"> <property name="driver" value="${driverClass}" /> <property name="url" value="${url}" /> <property name="username" value="${user}" /> <property name="password" value="${password}" /> </dataSource> </environment> </environments> <!-- 使用接口映射 配置查询语句 --> <mappers> <!-- 注册接口 如果使用的是注解则不需要mappers文件--> <!-- 有两种方式配置mapper 第一种 如果有mapper.xml文件直接配置该文件 <mapper resource="mapper 文件的类路径"> 第二种 配置接口的全类名 重点:如果配置的是接口的全类名则mapper.xm文件的名字必须与接口名相同 <mapper class="接口的全类名"> --> <mapper resource="cn/et/fuqiang/cache/xml/StudentMapper.xml"/> </mappers>
</configuration> |
2.接口映射的接口定义
package cn.et.fuqiang.cache.xml;
import cn.et.fuqiang.cache.entity.Student;
public interface MybatisCache { public Student queryStudentById(Integer stuid); public void updateStudent(Student student); }
|
3. 接口对应的mapper配置文件
StudentMapper.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="cn.et.fuqiang.cache.xml.MybatisCache">
<cache eviction="FIFO" flushInterval="10800000" size="512" readOnly="true" type="cn.et.fuqiang.cache.xml.RedisCache"></cache> <select id="queryStudentById" resultType="student" > select * from student where stuid=#{0} </select> <update id="updateStudent" > update student set stuname=#{stuname},gid=#{gid} where stuid=#{stuid} </update>
</mapper>
|
4. 实体类
Student 实体类 |
package cn.et.fuqiang.cache.entity;
import java.io.Serializable;
public class Student implements Serializable { /** * */ private static final long serialVersionUID = 1L; private Integer stuid;//学生id private String stuname;//学生姓名 private Integer gid;//班级id public Student() {} public Student(Integer stuid, String stuname, Integer gid) { super(); this.stuid = stuid; this.stuname = stuname; this.gid = gid; } public Integer getStuid() { return stuid; } public void setStuid(Integer stuid) { this.stuid = stuid; } public String getStuname() { return stuname; } public void setStuname(String stuname) { this.stuname = stuname; } public Integer getGid() { return gid; } public void setGid(Integer gid) { this.gid = gid; }
}
|
5. 测试运行的代码
MybatisCacheTest |
package cn.et.fuqiang.cache.xml;
import java.io.InputStream;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test;
import cn.et.fuqiang.cache.entity.Student;
public class MybatisCacheTest { public SqlSession getSession(){ //mybatis的配置文件 String resource = "mybatis.xml"; //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件) InputStream is = MybatisCacheTest.class.getResourceAsStream(resource); //构建sqlSession的工厂 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); //使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件) //Reader reader = Resources.getResourceAsReader(resource); //构建sqlSession的工厂 //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); //创建能执行映射文件中sql的sqlSession return sessionFactory.openSession(); } public SqlSessionFactory getSqlSessionFactory(){ //mybatis的配置文件 String resource = "mybatis.xml"; //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件) InputStream is = MybatisCacheTest.class.getResourceAsStream(resource); //构建sqlSession的工厂 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); //使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件) //Reader reader = Resources.getResourceAsReader(resource); //构建sqlSession的工厂 //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); //创建能执行映射文件中sql的sqlSession return sessionFactory; } //@Test public void firstGradeCache(){ /* 一级缓存 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象, 在对象中有个(内存区域)数据结构(HashMap)用于存储缓存数据。 不同的SqlSession之间缓存数据区域 (HashMap)是互不影响的。
作用域 一级缓存的作用域是同一个SqlSession在同一个SqlSession中两次执行相同的sql语句 第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据, 将不再从数据库查询,从而提高查询效率。当一个SqlSession结束后该SqlSession 结束后该SqlSession中的一级缓存也就不存在了。(Mybatis默认开启一级缓存) */ //获取一个session会话 SqlSession session = getSession(); //获取一个执行sql语句的映射接口 A MybatisCache firstGradeCacheA = session.getMapper(MybatisCache.class); //获取一个执行sql语句的映射接口 B MybatisCache firstGradeCacheB = session.getMapper(MybatisCache.class);
//通过A和B执行同一条sql Student studentA = firstGradeCacheA.queryStudentById(1); Student studentB = firstGradeCacheB.queryStudentById(1); //通过log4j发现sql语句执行了一次 ,并且两个对象相同。说明是第一次查询存入到内存里面的对象 System.out.println(studentA==studentB);
}
//@Test public void firstGradeCacheCommit(){ /* 一级缓存 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象, 在对象中有个(内存区域)数据结构(HashMap)用于存储缓存数据。 不同的SqlSession之间缓存数据区域 (HashMap)是互不影响的。
作用域 一级缓存的作用域是同一个SqlSession在同一个SqlSession中两次执行相同的sql语句 第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据(session在期间该session没有commit), 将不再从数据库查询,从而提高查询效率。(Mybatis默认开启一级缓存) 当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
注意: 如果SqlSession去执行commit操作(执行插入、更新、删除), 会清空SqlSession中的一级缓存,这样做的目的为了让缓存中的数据是最新的信息,避免脏读 */ //获取一个session会话 SqlSession session = getSession(); //获取一个执行sql语句的映射接口 A MybatisCache firstGradeCacheA = session.getMapper(MybatisCache.class); //获取一个执行sql语句的映射接口 update MybatisCache update= session.getMapper(MybatisCache.class); //获取一个执行sql语句的映射接口 A MybatisCache firstGradeCacheB = session.getMapper(MybatisCache.class); //让A先查询并 获取实体1 Student studentA = firstGradeCacheA.queryStudentById(1); //创建一个修改的数据 Student updateStudent = new Student(1,"wahaha",2); //然后在用更新的执行接口修改一下数据 update.updateStudent(updateStudent); //然后提交一下会话 session.commit(); //再查一下相同的用户 Student studentB = firstGradeCacheB.queryStudentById(1); //会发现sql语句执行了两次,而且返回的对象也不是同一个地址了 System.out.println(studentA==studentB);
} /**
二级缓存 二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace, 不同的sqlSession多次执行相同namespace下的Sql语句第一次执行完毕会将数据库中查询的数据写到缓存(内存), 第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。 所以说二级缓存是跨SqlSession的 Mybatis默认是开启二级缓存的,但是需要在mapper.xml中配置cache标签才能使用。 如果缓存中有数据就不用从数据库中获取,大大提高系统性能 注意: 要使用前一个session查询过的值,必须要等前一个session 关闭后才可以获取到 否则该值还在一级缓存中,因为mybatis的一级缓存是默认的无法取消,这也是为了数据安全性考虑
同一个SqlSessionFactory可以获取不同的session会话
使用二级缓存 1.在核心配置文件Mybatis.xml中配置<setting>标签 (默认是开启的但是必须配置cache标签才能使用所以可以不配置该配置文件) <!-- 二级缓存的配置 --> <settings> <setting name="cacheEnabled" value="true"/> </settings> 2.在Mapper.xml中开启二级缓存,当该namespace下的 sql执行完成会存储到它的缓存区域 在mapper下第一行配置 <mapper namespace=""> <!-- 配置二级缓存 eviction 回收策略 默认为 LRU 【默认】LRU——最近最少使用的:移除最长时间不被使用的对象 【新】LFU——最近一段时间使用次数最少的(部分版本不能使用) FIFO——先进先出的:按对象进入缓存的顺序来移除他们 SOFT——软引用:移除基于垃圾回收器状态和软引用规则的对象 WEAK——弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 flushInterval (刷新间隔) 可以被设置为任意的正整数(60*60*1000这种形式是不允许的),而且它们代表一个合理的毫秒形式的时间段。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。 size (引用数目) 可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024. readOnly (只读) 属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过发序列化)。这会慢一些,但是安全,因此默认是false。 type="" 自定义缓存的类 自定义缓存需要实现cache接口,并且重写其中的方法,就可以使用第三方缓存了 --> <cache eviction="FIFO" flushInterval="10800000" size="512" readOnly="true"></cache>
......... </Mapper>
*/ @Test public void secondGradeCacheNoCommitAndClose(){
//获取一个session工厂 SqlSessionFactory factory = getSqlSessionFactory(); //获取两个不同的会话 SqlSession sessionA = factory.openSession(); SqlSession sessionB = factory.openSession(); //执行接口 MybatisCache firstGradeCacheA = sessionA.getMapper(MybatisCache.class); MybatisCache firstGradeCacheB= sessionB.getMapper(MybatisCache.class);
//查询id为1的学生 Student studentA=firstGradeCacheA.queryStudentById(1); Student studentB=firstGradeCacheB.queryStudentById(1); //比较两个结果相同(如果是使用了序列化和反序列化则不相同) System.out.println(studentA==studentB);
//如果不提交不关闭则数据一直在一级缓存中,会发起两个sql语句 } @Test public void secondGradeCacheByCommit(){
//获取一个session工厂 SqlSessionFactory factory = getSqlSessionFactory(); //获取两个不同的会话 SqlSession sessionA = factory.openSession(); SqlSession sessionB = factory.openSession(); //执行接口 MybatisCache firstGradeCacheA = sessionA.getMapper(MybatisCache.class); MybatisCache firstGradeCacheB= sessionB.getMapper(MybatisCache.class);
//查询id为1的学生 Student studentA=firstGradeCacheA.queryStudentById(1); //关闭A会话,将一级缓存清空,存入二级缓存 sessionA.commit(); Student studentB=firstGradeCacheB.queryStudentById(1); //比较两个结果相同(如果是使用了序列化和反序列化则不相同) System.out.println(studentA==studentB); //通过log4j显示的运行结果可以看出语句值查询了一次 } @Test public void secondGradeCacheClose(){
//获取一个session工厂 SqlSessionFactory factory = getSqlSessionFactory(); //获取两个不同的会话 SqlSession sessionA = factory.openSession(); SqlSession sessionB = factory.openSession(); //执行接口 MybatisCache firstGradeCacheA = sessionA.getMapper(MybatisCache.class); MybatisCache firstGradeCacheB= sessionB.getMapper(MybatisCache.class);
//查询id为1的学生 Student studentA=firstGradeCacheA.queryStudentById(1); //关闭A会话,将一级缓存清空,存入二级缓存 sessionA.close(); Student studentB=firstGradeCacheB.queryStudentById(1); //比较两个结果相同(如果是使用了序列化和反序列化则不相同) System.out.println(studentA==studentB); //通过log4j显示的运行结果可以看出语句值查询了一次 } @Test public void queryStudentRedis(){ /** *使用二级缓存时前面的方法查询过该用户后,该方法查询直接从redis中获取,不发起sql语句 */ SqlSession session = getSession(); MybatisCache firstGradeCache = session.getMapper(MybatisCache.class);
Student student = firstGradeCache.queryStudentById(2); session.close(); } /** * 提交一个数据看是否会将二级缓存清空 */ @Test public void commitData(){ SqlSession session = getSession(); MybatisCache update= session.getMapper(MybatisCache.class);
Student updateStudent = new Student(1,"wahaha",2); update.updateStudent(updateStudent); session.commit(); }
} |
数据库的中的表可以根据实体的字段创建
自定义缓存类代码在上面,三、自定义缓存的实现中有代码