mybatis---缓存
1、缓存介绍
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
2、一级缓存
特性
1、默认开启,也可以手动去关闭 localCacheScope = STATEMENT。
2、作用域是基于 sqlSession(默认),一次数据库操作会话。
3、缓存默认实现类 PerpetualCache ,使用map进行存储缓存,其中:key ==> hashcode + sqlId + sql + hashcode + environments的默认id。
4、查询完就进行存储。
5、先去二级缓存拿,再去一级缓存拿。
失效情况
1、不同的 sqlSession 会使sql缓存失效。创建两个sqlSession看日志即可。
2、同一个 sqlSession ,但是sql语句不一样。调用两个不同的sql看日志即可。
3、同一个 sqlSession ,中间执行了增删改,缓存也会失效,即使不是与当前sql有关的。
4、同一个 sqlSession ,中间手动清除了缓存,缓存会失效。如:sqlSession.clearCache();
/**
* 一级缓存
* 特性:
* 1、默认开启,也可以手动去关闭 localCacheScope = STATEMENT
* 2、作用域是基于 sqlSession(默认),一次数据库操作会话
* 3、缓存默认实现类 PerpetualCache ,使用map进行存储缓存
* key ==> hashcode + sqlId + sql + hashcode + environments的默认id
* 4、查询完就进行存储
* 5、先去二级缓存拿,再去一级缓存拿
* 失效情况:
* 1、不同的 sqlSession 会使sql缓存失效
* 2、同一个 sqlSession ,但是sql语句不一样
* 3、同一个 sqlSession ,中间执行了增删改,缓存也会失效
* 4、同一个 sqlSession ,中间手动清除了缓存,缓存会失效
*/
@Test
public void select01() {
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept();
dept.setDeptId(1);
List<Dept> deptList = mapper.selectDept(dept);
System.out.println(deptList);
// 清除缓存
session.clearCache();
List<Dept> deptList2 = mapper.selectDept(dept);
System.out.println(deptList2);
}
3、二级缓存
3.1、二级缓存初步使用
特性
1、默认开启了,但是没有实现,如果我们需要用,需要我们自己去实现。
2、作用域:基于全局范围,应用级别的。
3、缓存默认实现类 PerpetualCache ,使用map进行存储,但是二级缓存根据不同的mapper.xml的命名空间多包了一层
org.apache.ibatis.session.Configuration#caches 里面的key = mapper.xml的命名空间 value=PerpetualCache.map
key ==> hashcode + sqlId + sql + hashcode + environments的默认id
4、事务提交的时候进行存储(sqlSession关闭时)。
5、先去二级缓存拿,再去一级缓存拿。
实现步骤
1、开启二级缓存,只是为了醒目,二级缓存已经默认开启 。
<settings>
<!-- 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
2、因为二级缓存是针对于mapper.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="cool.ale.mapper.DeptMapper">
<cache></cache>
3、如果存储的是JavaBean,需要实现序列化接口 implements Serializable
如果配置成功了,会在日志中打印出命中率 ==> 从缓存中取出的次数/查询总次数。
public class Dept implements Serializable {
}
测试类
package cool.ale.tests;
import cool.ale.mapper.DeptMapper;
import cool.ale.mapper.EmpMapper;
import cool.ale.pojo.Dept;
import cool.ale.pojo.DeptEmpDTO;
import cool.ale.pojo.EmpDeptDTO;
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.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
private SqlSessionFactory sqlSessionFactory = null;
@Before
public void before() throws IOException {
// 从 xml中构建 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* 二级缓存
* 特性:
* 1、默认开启了,但是没有实现,如果我们需要用,需要我们自己去实现
* 2、作用域:基于全局范围,应用级别的
* 3、缓存默认实现类 PerpetualCache ,使用map进行存储,但是二级缓存根据不同的mapper.xml的命名空间多包了一层
* org.apache.ibatis.session.Configuration#caches 里面的key = mapper.xml的命名空间 value=PerpetualCache.map
* key ==> hashcode + sqlId + sql + hashcode + environments的默认id
* 4、事务提交的时候进行存储(sqlSession关闭时)
* 5、先去二级缓存拿,再去一级缓存拿
* 实现:
* 1、开启二级缓存,只是为了醒目,二级缓存已经默认开启 <setting name="cacheEnabled" value="true"/>
* 2、因为二级缓存是针对于mapper.xml文件的,所以在需要使用到二级缓存的文件中加入 <cache></cache>
* 3、如果存储的是JavaBean,需要实现序列化接口 implements Serializable
* 如果配置成功了,会在日志中打印出命中率 ==> 从缓存中取出的次数/查询总次数
* 失效:
* 1、同一个命名空间进行了增删改操作,会导致二级缓存失效
* 如果不想失效,可以在增删改上设置一个 flushCache="false"
* flushCache 是否执行完本条sql就要清除缓存,默认为true
* 2、让查询不缓存数据到二级缓存中 useCache="false"
* useCache是否使用缓存,默认为true
* 3、如果希望当一个命名空间下执行了增删改操作,同时清空另一个命名空间的缓存
* 可以设置 <cache-ref namespace="cool.ale.mapper.DeptMapper"/>
* 但是目前只能设置一个,弊端。
*/
@Test
public void select02() {
try (SqlSession session = sqlSessionFactory.openSession(true)) {
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept();
dept.setDeptId(1);
List<Dept> deptList = mapper.selectDept(dept);
System.out.println(deptList);
}
try (SqlSession session = sqlSessionFactory.openSession(true)) {
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept();
dept.setDeptId(1);
List<Dept> deptList = mapper.selectDept(dept);
System.out.println(deptList);
}
}
@Test
public void select03() {
SqlSession session = sqlSessionFactory.openSession();
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept();
dept.setDeptId(1);
List<Dept> deptList = mapper.selectDept(dept);
System.out.println(deptList);
session.commit();
SqlSession session2 = sqlSessionFactory.openSession(true);
DeptMapper mapper2 = session.getMapper(DeptMapper.class);
Dept dept2 = new Dept();
dept2.setDeptId(1);
List<Dept> deptList2 = mapper2.selectDept(dept2);
System.out.println(deptList2);
}
}
失效情况
1、同一个命名空间进行了增删改操作,会导致二级缓存失效。
如果不想失效,可以在增删改上设置一个 flushCache=“false”
flushCache 是否执行完本条sql就要清除缓存,默认为true
<!-- flushCache属性当设置为false时,就算执行了这条insert语句,也不会清空mybatis二级缓存,默认为true -->
<insert id="insertDept" flushCache="false"></insert>
2、让查询不缓存数据到二级缓存中 useCache=“false”
useCache是否使用缓存,默认为true
这个里面也有 flushCache 属性,默认值为false
<!-- useCache是否使用缓存,默认为true
flushCache 是否执行完本条sql就要清除缓存,默认为false
-->
<select id="selectDept" resultMap="BaseMap" useCache="false" flushCache="false">
select * from dept
<where>
<if test="deptId != null and deptId != ''">
and dept_id = #{deptId}
</if>
<if test="deptName != null and deptName != ''">
and dept_name = #{deptName}
</if>
</where>
</select>
3、如果希望当一个命名空间下执行了增删改操作,同时清空另一个命名空间的缓存
可以设置
但是目前只能设置一个,弊端。
<?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="cool.ale.mapper.EmpMapper">
<cache></cache>
<!-- 如果这个命名空间执行了增删改操作,将会同步清空 DeptMapper 命名空间下的缓存 -->
<cache-ref namespace="cool.ale.mapper.DeptMapper"/>
3.2、cache的参数
<cache
type="org.apache.ibatis.cache.impl.PerpetualCache"
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
type:默认的缓存实现类,可以改变指定第三方的缓存实现类。
eviction:
LRU :– 最近最少使用:移除最长时间不被使用的对象。
FIFO :– 先进先出:按对象进入缓存的顺序来移除它们。
SOFT :– 软引用:基于垃圾回收器状态和软引用规则移除对象
WEAK :– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushInterval:(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size:(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly:(只读)属性可以被设置为 true 或 false。当设置为true时,mybatis会默认这个变量不会被修改,就会将地址直接给需要引用此变量的地方,所以获取的更快,但也会引起脏数据。false是会copy一份。
4、整合第三方缓存
4.1、整合Windows版本的Redis缓存
4.1.1、 在github上下载一个windows版本的redis:windows版本的redis地址。
进来之后点击note进入之后下载一个解压版本的。
解压版图示:
4.1.2、开始搭建
1、在mapper.xml文件中添加引用redis的类
<?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="cool.ale.mapper.DeptMapper">
<!-- 调用redis缓存 -->
<cache type="org.mybatis.caches.redis.RedisCache" />
<resultMap id="BaseMap" type="cool.ale.pojo.Dept">
<result column="DEPT_ID" property="deptId"></result>
<result column="DEPT_NAME" property="deptName"></result>
</resultMap>
<select id="selectDept" resultMap="BaseMap">
select * from dept
<where>
<if test="deptId != null and deptId != ''">
and dept_id = #{deptId}
</if>
<if test="deptName != null and deptName != ''">
and dept_name = #{deptName}
</if>
</where>
</select>
</mapper>
2、在resouce文件夹创建redis.properties文件
redis.host=localhost
redis.port=6379
redis.connectionTimeout=5000
redis.soTimeout=5000
redis.password=
redis.database=0
redis.clientName=
3、写出测试类
package cool.ale.tests;
import cool.ale.mapper.DeptMapper;
import cool.ale.mapper.EmpMapper;
import cool.ale.pojo.Dept;
import cool.ale.pojo.DeptEmpDTO;
import cool.ale.pojo.EmpDeptDTO;
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.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MybatisTest {
private SqlSessionFactory sqlSessionFactory = null;
@Before
public void before() throws IOException {
// 从 xml中构建 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void select02() {
try (SqlSession session = sqlSessionFactory.openSession(true)) {
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept();
dept.setDeptId(1);
List<Dept> deptList = mapper.selectDept(dept);
System.out.println(deptList);
}
try (SqlSession session = sqlSessionFactory.openSession(true)) {
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept();
dept.setDeptId(1);
List<Dept> deptList = mapper.selectDept(dept);
System.out.println(deptList);
}
}
@Test
public void select03() {
SqlSession session = sqlSessionFactory.openSession();
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept();
dept.setDeptId(1);
List<Dept> deptList = mapper.selectDept(dept);
System.out.println(deptList);
session.commit();
SqlSession session2 = sqlSessionFactory.openSession(true);
DeptMapper mapper2 = session2.getMapper(DeptMapper.class);
Dept dept2 = new Dept();
dept2.setDeptId(1);
List<Dept> deptList2 = mapper2.selectDept(dept2);
System.out.println(deptList2);
}
}