MyBatis缓存级别
1. 概述
缓存
,顾名思义其实就是就是为了提高查询效率。MyBatis的缓存同样是如此。那么接下来让我们看下MyBatis缓存到底是怎么回事。
点击 此连接 查看Demo 源码
2. 环境准备
mapper
public interface PlanTaskMapper {
List<PlanTask> findAll(String projectId);
}
pojo
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class PlanTask implements Serializable {
private String projectId;
private String planContent;
private String id;
}
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>
<properties resource="jdbc.properties"/>
<typeAliases>
<!--包扫描起别名 类的短路径名首字母小写-->
<package name="plus.chendd.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<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>
<!--加载mapper映射文件-->
<mappers>
<!--通过包扫描加载所有的mapper-->
<package name="plus.chendd.mapper"/>
</mappers>
</configuration>
初步测试案例
public class Test001 {
private SqlSession sqlSession;
@Before
public void init() {
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
InputStream resourceAsStream;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
throw new RuntimeException(e);
}
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
sqlSession = build.openSession();
}
public void print(List<PlanTask> list) {
for (Object o : list) {
System.out.println(o);
}
}
@Test
public void test01() {
PlanTaskMapper mapper = sqlSession.getMapper(PlanTaskMapper.class);
List<PlanTask> all = mapper.findAll("11111");
print(all);
}
@After
public void after() {
sqlSession.close();
}
}
3. 一级缓存
3.1 实例
public void test01() {
PlanTaskMapper mapper = sqlSession.getMapper(PlanTaskMapper.class);
List<PlanTask> all = mapper.findAll("11111");
print(all);
List<PlanTask> all1 = mapper.findAll("11111");
print(all1);
}
通过上述示例可以看到,mybatis具有缓存的。因为在第二次进行查询的时候,并没有连接数据库而是直接返回值
3.2 一级缓存的原理
一级缓存是基于sqlSession进行缓存的,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口
缓存生成的规则是:namespace + SQL.id + args + offset. 通过一个hash函数来生成一个值。将查询的结果作为key。最后以key/value 的形式存储到内存中
3.3 反向验证
@Test
public void test02() {
PlanTaskMapper mapper = sqlSession.getMapper(PlanTaskMapper.class);
List<PlanTask> all = mapper.findAll("11111");
print(all);
PlanTaskMapper mapper1 = sqlSession01.getMapper(PlanTaskMapper.class);
List<PlanTask> all1 = mapper1.findAll("11111");
print(all1);
}
通过上图可以看到,如果使用两个不同的sqlSession来执行mapper的话,其结果是不具备缓存的。
3.4 清除缓存
当发生编辑操作,进行commit的时候,会清除sqlSession 缓存。
当我们添加如下代码:
sqlSession.commit();
重新执行后,会出现如下截图:(出现了两次sql 查询)
4. 二级缓存
- 二级缓存是以namespace为标记的缓存,可以是由一个SqlSessionFactory创建的SqlSession之间共享缓存数据。默认并不开启。下面的代码中创建了两个SqlSession,执行相同的SQL语句,尝试让第二个SqlSession使用第一个SqlSession查询后缓存的数据。
- 要求实体类必须实现序列化接口
其实就是获取两个sqlSession 来进行数据查询,在某种情况下两个sqlSession可以实现数据共享
public void testFindDeptByDetpno() {
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.findByEmpno(7521);
System.out.println(emp);
// SqlSession提交之后,才会将查询的结果放入二级缓存
sqlSession.commit();
EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
Emp emp2 = mapper2.findByEmpno(7521);
System.out.println(emp2);
}
注意其中的commit(),执行该命令后才会将该SqlSession的查询结果从一级缓存中放入二级缓存,供其他SqlSession使用。另外执行SqlSession的close()也会将该SqlSession的查询结果从一级缓存中放入二级缓存。两种方式区别在当前SqlSession是否关闭了
4.1 二级缓存开启方式
- 第一步:
全局开关:在sqlMapConfig.xml文件中的标签配置开启二级缓存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 第二步:
分开关:在要开启二级缓存的mapper文件中开启缓存
<mapper namespace="com.msb.mapper.EmployeeMapper">
<cache/>
</mapper>
- 第三步:
二级缓存未必完全使用内存,有可能占用硬盘存储,缓存中存储的JavaBean对象必须实现序列化接口
public class Emp implements Serializable { }
- 第四步:
如果在加入Cache元素的前提下让个别select 元素不使用缓存,可以使用useCache属性,设置为false。useCache控制当前sql语句是否启用缓存 flushCache控制当前sql执行一次后是否刷新缓存
<select id="findByEmpno" resultType="emp" useCache="true">
经过设置后,查询结果如图所示。发现第一个SqlSession会首先去二级缓存中查找,如果不存在,就查询数据库,在commit()或者close()的时候将数据放入到二级缓存。第二个SqlSession执行相同SQL语句查询时就直接从二级缓存中获取了
5. 总结
以上就是对MyBatis中 一级二级缓存的认识,尤其是一级缓存,有效的利用缓存可以减少跟数据库IO 的操作,提高效率。