MyBatis的查询缓存机制,根据缓存区的作用域(生命周期)可以划分为2种:一级查询缓存和二级查询缓存
MyBatis一级查询缓存是基于org.apache.ibatis.catch.impl.PerpetualCache类的HsahMap本地缓存,其作用域是SqlSession。
当一个SqlSessoin结束后,该SqlSession中的一级查询缓存也就不存在了。
MyBatis默认一级查询缓存是开启状态,且不能关闭。
一个SqlSession操作一个mapper.xml文件(sqlSession.getMapper(IStudentDao.class)),也就是说一个SqlSession对应一个namespace。但是一个namespace可以对多个SqlSession
@Test
public void test01() {
Student student = dao.selectById(2);
System.out.println(student);
Student student2 = dao.selectById(2);
System.out.println(student2);
}
[DEBUG] ==> Preparing: select id,name,age,score from student where id = ?
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <== Columns: id, name, age, score
[TRACE] <== Row: 2, 李四, 24, 94.5
[DEBUG] <== Total: 1
Student [id=2, name=李四, age=24, score=94.5]
Student [id=2, name=李四, age=24, score=94.5]
第二次
查询没输出
sql语句,说明
没有查询
数据库,直接从
SqlSession的缓存中
查询数据
SqlSession缓存的底层实现是一个Map,Map的value是查询结果
Map的key,即查询依据,不同的ORM框架,查询的依据是不同的
MyBatis的查询依据是:SQL的id + SQL语句
Hibernate的查询依据是:查询结果对象的id
@Test
public void test03() {
Student student = dao.selectById(2);
System.out.println(student);
// 增删改操作都会刷新SqlSession一级缓存【刷新缓存即清空缓存】,肯定要刷新缓存,不然从缓存读取可能是脏数据
dao.insertStudent(new Student("哈哈", 91, 100));
// sqlSession.commit(); //不提交不会写入数据库【不提交也同样会刷新缓存】
Student student2 = dao.selectById(2);
System.out.println(student2);
}
[DEBUG] ==> Preparing: select id,name,age,score from student where id = ?
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <== Columns: id, name, age, score
[TRACE] <== Row: 2, 李四, 24, 94.5
[DEBUG] <== Total: 1
Student [id=2, name=李四, age=24, score=94.5]
[DEBUG] ==> Preparing: insert into student(name,age,score) values(?,?,?)
[DEBUG] ==> Parameters: 哈哈(String), 91(Integer), 100.0(Double)
[DEBUG] <== Updates: 1
[DEBUG] ==> Preparing: select id,name,age,score from student where id = ?
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <== Columns: id, name, age, score
[TRACE] <== Row: 2, 李四, 24, 94.5
[DEBUG] <== Total: 1
Student [id=2, name=李四, age=24, score=94.5]
MyBatis查询缓存的作用域是根据映射文件mapper的namespace划分的,相同namespace的mapper查询数据存放在同一个缓存区域,不同namespace下的数据互不干扰。
无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。
不同之处在于,SqlSession一旦关闭,则SqlSession中缓存的数据将不存在,即一级缓存就不存在。而二级缓存的生命周期会与整个应用同步。
开启内置二级缓存步骤:
public class Student implements Serializable {
…………
}
eviction:逐出策略。
FIFO:先进先出
LRU: 未被使用时间最长的
flushInterval:刷新缓存的时间间隔,单位毫秒。刷新即清空缓存。一般不指定,即当执行增删改操作时刷新缓存
readOnly:设置缓存中的对象是否只读
size:缓存中对象最多可以存放的个数
@Test
public void test01() {
sqlSession = MyBatisUtils.getSqlSession();
dao = sqlSession.getMapper(IStudentDao.class);
Student student = dao.selectById(2);
System.out.println(student);
sqlSession.close();
sqlSession = MyBatisUtils.getSqlSession();
dao = sqlSession.getMapper(IStudentDao.class);
Student student2 = dao.selectById(2);
System.out.println(student2);
}
sqlSession.close(); 关闭之后,SqlSession一级缓存也关闭了。
所以不会从一级缓存查询,不开启二级缓存的话,会去数据库查
[DEBUG] Cache Hit Ratio [com.xinm.mybatis.dao.IStudentDao]: 0.0
[DEBUG] ==> Preparing: select id,name,age,score from student where id = ?
[DEBUG] ==> Parameters: 2(Integer)
[TRACE] <== Columns: id, name, age, score
[TRACE] <== Row: 2, 李四, 24, 94.5
[DEBUG] <== Total: 1
Student [id=2, name=李四, age=24, score=94.5]
[DEBUG] Cache Hit Ratio [com.xinm.mybatis.dao.IStudentDao]: 0.5
Student [id=2, name=李四, age=24, score=94.5]
CaChe Hit Ratio
缓存命中率,可以看出
二级缓存已经开启。
第一次命中率 0.0(没命中) ,第二次命中率0.5(命中) ,且没有执行Sql去数据库中查询。说明从缓存中查询
当然可以单独设置sql查询不使用缓存,那么就不会缓存数据,肯定也不会去缓存中查询数据。
当然这里说的缓存是二级缓存。一级缓存是MyBatis默认开启且不能关闭,也不能设置的。
当然也可以单独设置sql增删改操作不刷新缓存,当然这里说的缓存同样是二级缓存。
insert into student(name,age,score) values(#{name},#{age},#{score})
一级缓存没有配置文件可以设置,因为MyBatis默认SqlSession一级缓存开启,且不能关闭,不能设置。
二级缓存其实很少用,因为很多表都存在关联关系,必有关联查询。一般只对表查询【很】多更新【极】少的情况
ehCache核心jar包下面有一个ehcache-failsafe.xml。拷贝重命名为ehcache.xml,放在类路径下
在映射文件mapper中的
默认为MyBatis内置的二级缓存org.apache.ibatis.catch.impl.PerpetualCache。
mybatis.xml主配置文件