许多运用程序为了提高性能而增加缓存,特别是从数据库中获取的数据,如果缓存中没有,就到表中去查询,表查询的数据添加到缓存中去,下次查询是直接从缓存中读取,Mybatis 包含一个非常强大的查询缓存特性,它可以非常方便的配置和定制,缓存可以极大的提升查询效率。就如同第一次访问某网站时耗时会就一下感觉到卡,关闭网页再访问相同网页时就流畅了很多,所以缓存机制是很重要的。
一、mybatis 的一级缓存
在默认情况下,mybatis 的一级缓存是默认开启的,类似与hiberante,所谓一级缓存也就是基于一个 sqlsession 的查询语句, 即 session 级别的缓存,非全局缓存或者称为非二级缓存。
一级缓存(local cache),即本地缓存,作用域默认为 sqlsession ,当 Session close 后,该 session 中所有 Cache 将被清空,本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存。
- 一级缓存失效的四种情况:
- 不同 SqlSession 对应不同的一级缓存
- 同一个 SqlSession 但是查询条件不同
- 同一个 SqlSession 两次查询期间执行了任何一次增删改的操作
- 同一个 SqlSession 两次查询手动清空了缓存
UserMapping.xml
<resultMap type="cn.softjx.modle.User" id="UserMap">
<result column="t_id" property="id"/>
<result column="t_username" property="username"/>
<result column="t_password" property="password"/>
<result column="t_sid" property="sid"/>
</resultMap>
<select id="selectAll2" resultMap="UserMap" >
select t_id,t_username,t_password from t_user;
</select>
映射文件不做任何处理,mybatis默认一级缓存
Test.java
@Test
public void testSelect(){
try {
List<User> user=mapper.selectAll2();
for(User users:user){
System.out.println(users.getPassword()+" "+users.getUsername()+" "+users.getSid());
}
List<User> user1=mapper.selectAll2();
for(User users:user1){
System.out.println(users.getPassword()+" "+users.getUsername()+" "+users.getSid());
}
} finally{
// TODO Auto-generated catch block
session.close();
}
}
声明了两个不同的 user 对象,但都是在同一个 Session 内执行查询的,代码虽然执行了两次数据库的查询,但是实际上只是在数据库查询了一次并将其缓存在同一 Session 中,再使用同一个 Session 查询同一字段时直接读取一级缓存中的数据内容。
总共打印了18条数据,从 log4j 的打印结果可以看出从数据库中查询出9条数据,并且只是发送了一次 SQL 语句。
注意:
1. 用同一 Session 但查询条件不同缓存失效
2. 用同一 Session 在第二次查询前执行了任何造成数据库改动的操作(CRDU)
再使用同一句查询语句缓存失效
二、Mybatis 二级缓存
如果要实现 mybatis 的二级缓存,二级缓存需要手动开启和配置,它是基于 namespace 级别的缓存,全局作用域缓存,一般来说有两种方式 :
- 采用 mybatis 内置的 cache 机制
- mybatis 定义了缓存接口 Cache,我们可以通过实现 Cache 接口来定义二级缓存,可采用第三方 Cache 框架。
- 采用 mybatis 内置的 cache 机制
在表映射文件中加入 <cache /> 语句,并且相应的 model 要实现 java 的 Serializable 接口,因为二级缓存就是序列化和反序列化的过程,<cache /> 表示:
- 所有在映射文件里的 select 语句都将被缓存
- 所有在映射文件里的 insert,update 和 delete 语句会被清空缓存。
- 缓存不会被设定的时间所清空。
- 每个缓存可以存储 1024 个列表或对象的引用
- 缓存将作为 “读/写” 缓存,意味着获取对象不是共享的且对调用者是安全的,不会有其他的调用者或线程潜在修改。
使用步骤 :
- 全局配置文件中开启二级缓存,默认是打开的
<setting name="cacheEnabled" value="ture" /> - 需要使用二级缓存的映射文件处理使用 cache 配置缓存
- POJO需要实现 Serializable 接口
- 如果会话关闭:一级缓存的数据会保存到二级缓存中,新的会话查询信息就可以参照二级缓存中的内容,二级缓存会在 SqlSession 关闭或提交后才会生效。
bean.java 实现序列化
public class User implements Serializable{
private Integer id;
private String username;
private String password;
private Integer sid;
private School school;
public User() {
}
}
config.xml
<settings>
<setting name="cacheEnabled" value="true"></setting>
</settings>
Mapping.xml 配置 <cache /> 标签
<mapper namespace="cn.softjx.dao.inter.UserMapping">
<cache />
<resultMap type="cn.softjx.modle.User" id="UserMap">
<result column="t_id" property="id"/>
<result column="t_username" property="username"/>
<result column="t_password" property="password"/>
<result column="t_sid" property="sid"/>
......
在 Test.java 中声明三个不同的 session 执行相同的查询
User useBean=new User();
useBean.setId(4);
List<User> user=mapper.selectAll3(useBean);
for(User users:user){
System.out.println(users.getPassword()+" "+users.getUsername()+" "+users.getSid());
}
session.close();
UserMapping mapper1=session1.getMapper(UserMapping.class);
List<User> user1=mapper1.selectAll3(useBean);
for(User users:user1){
System.out.println(users.getPassword()+" "+users.getUsername()+" "+users.getSid());
}
session1.close();
UserMapping mapper2=session2.getMapper(UserMapping.class);
List<User> user2=mapper2.selectAll3(useBean);
for(User users:user2){
System.out.println(users.getPassword()+" "+users.getUsername()+" "+users.getSid());
}
session2.close();
虽然使用了不同的 Session 但只发送了一条 SQL。即不同的sqlSession可以共享一个 Mapping
日志中有一个 Hit Ratio 命中率,它是如何计算的呢。第一次查询由于缓存中是没有数据的所以是 0.0,第二次查询从缓存中取 1-(1/2)=0.5,第三次查询
1-(1/3)=0.666666
cache 标签中还有几个属性 eviction , flushInterval ,readOnly
- eviction:
- LRU :最近不使用的缓存数据,移出最长时间不被使用的数据
- FIFO :先进先出
- SOFT : 软引用,移除基于垃圾回收器状态和软引用规则对象
- WEAK :软引用,更积极的垃圾回收器状态和软引用规则对象
,默认是 LRU
flushInterval :缓存刷新清空时间,默认不刷新
readOnly :是否进行只读操作,如果只进行只读操作当数据库内的值发送变化时,缓存值马上改变得到的数据还是原有的为进行修改的数据。
上篇:Mybatis (三)动态sql:http://www.jianshu.com/p/71c0cdd9c249