是一种临时存储少量数据至内存或者是磁盘的一种技术.减少数据的加载次数,可以降低工作量,提高程序响应速度
缓存的重要性是不言而喻的。mybatis的缓存将相同查询条件的SQL语句执行一遍后所得到的结果存在内存或者某种缓存介质当中,当下次遇到一模一样的查询SQL时候不在执行SQL与数据库交互,而是直接从缓存中获取结果,减少服务器的压力;尤其是在查询越多、缓存命中率越高的情况下,使用缓存对性能的提高更明显。
MyBatis允许使用缓存,缓存一般放置在高速读/写的存储器上,比如服务器的内存,能够有效的提供系统性能。MyBatis分为一级缓存和二级缓存,同时也可配置关于缓存设置。
一级存储是SqlSession上的缓存,二级缓存是在SqlSessionFactory(namespace)上的缓存。默认情况下,MyBatis开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据。
1.一级缓存
一级存储是SqlSession上的缓存,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口。
缓存中的数据使用键值对形式存储数据
namespace+sqlid+args+offset>>> hash值作为键,查询出的结果作为值
@Test public void testFindDeptByDetpno() { EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.findByEmpno(7521); System.out.println(emp); // 中间发生了增删改或者是调用了SqlSession调用了commit,会自动清空缓存 sqlSession.commit();// 增删改的时候调用 EmpMapper mapper2 = sqlSession.getMapper(EmpMapper.class); Emp emp2 = mapper2.findByEmpno(7521); System.out.println(emp2); System.out.println(emp==emp2); System.out.println(mapper==mapper2); }
2.二级缓存
二级缓存是以namespace为标记的缓存,可以是由一个SqlSessionFactory创建的SqlSession之间共享缓存数据。默认并不开启。下面的代码中创建了两个SqlSession,执行相同的SQL语句,尝试让第二个SqlSession使用第一个SqlSession查询后缓存的数据。要求实体类必须实现序列化接口
接口
public interface EmpMapper { Emp findByEmpno(int empno); }
映射文件
<mapper namespace="com.msb.mapper.EmpMapper"> <cache/> <select id="findByEmpno" resultType="emp" useCache="true" flushCache="false"> select * from emp where empno =#{empno} </select> </mapper>
测试代码
package com.msb.test; import com.msb.mapper.EmpMapper; import com.msb.pojo.Emp; 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.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; /** * @Author: Ma HaiYang * @Description: MircoMessage:Mark_7001 */ public class Test3 { private SqlSession sqlSession; private SqlSession sqlSession2; @Before public void init(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); sqlSession2=factory.openSession(); } @Test 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); } @After public void release(){ // 关闭SQLSession sqlSession.close(); sqlSession2.close(); } }
注意其中的commit(),执行该命令后才会将该SqlSession的查询结果从一级缓存中放入二级缓存,供其他SqlSession使用。另外执行SqlSession的close()也会将该SqlSession的查询结果从一级缓存中放入二级缓存。两种方式区别在当前SqlSession是否关闭了。
执行结果显示进行了两次对数据库的SQL查询,说明二级缓存并没有开启。需要进行如下步骤完成开启。
1) 全局开关:在sqlMapConfig.xml文件中的<settings>标签配置开启二级缓存
<settings> <setting name="cacheEnabled" value="true"/> </settings>
cacheEnabled的默认值就是true,所以这步的设置可以省略。
2) 分开关:在要开启二级缓存的mapper文件中开启缓存:
<mapper namespace="com.msb.mapper.EmployeeMapper"> <cache/> </mapper>
3) 二级缓存未必完全使用内存,有可能占用硬盘存储,缓存中存储的JavaBean对象必须实现序列化接口,
public class Emp implements Serializable { }
经过设置后,查询结果如图所示。发现第一个SqlSession会首先去二级缓存中查找,如果不存在,就查询数据库,在commit()或者close()的时候将数据放入到二级缓存。第二个SqlSession执行相同SQL语句查询时就直接从二级缓存中获取了。
注意:
1) MyBatis的二级缓存的缓存介质有多种多样,而并不一定是在内存中,所以需要对JavaBean对象实现序列化接口。
2) 二级缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响
3) 加入Cache元素后,会对相应命名空间所有的select元素查询结果进行缓存,而其中的insert、update、delete在操作是会清空整个namespace的缓存。
4) cache 有一些可选的属性 type, eviction, flushInterval, size, readOnly, blocking。
<cache type="" readOnly="" eviction=""flushInterval=""size=""blocking=""/>
5) 如果在加入Cache元素的前提下让个别select 元素不使用缓存,可以使用useCache属性,设置为false。useCache控制当前sql语句是否启用缓存 flushCache控制当前sql执行一次后是否刷新缓存
<select id="findByEmpno" resultType="emp" useCache="true" flushCache="false">