五、mybatis两级缓存
一级缓存:(本地缓存) sqlSession级别的缓存。
- 一级缓存是一直开启的。
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查数据库。
验证实例(实时使用上一篇的目录结构):
接口文件EmployeeMapper.java:
Employee getEmpById(int id);
映射文件EmployeeMapper.xml
1 <select id="getEmpById" parameterType="Integer" resultType="entity.Employee"> 2 select LAST_NAME AS 3 lastName,gender as gender,email as email from 4 tbl_employee where id =#{asdsdfsdf} 5 </select>
junit测试类:
1 @Test 2 public void test02() { 3 String resource = "mybatis-config.xml"; 4 SqlSession openSession = null; 5 try { 6 InputStream inputStream = Resources.getResourceAsStream(resource); 7 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 // 获取openSession 不会自动提交数据 9 openSession = sqlSessionFactory.openSession(true); 10 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); 11 Employee employee = mapper.getEmpById(1); 12 Employee employee1 = mapper.getEmpById(1); 13 System.out.println("测试一级缓存:" + employee); 14 System.out.println("测试一级缓存:" + employee1 + "," + (employee == employee1)); 15 } catch (Exception e) { 16 // TODO: handle exception 17 } finally { 18 if (openSession != null) { 19 openSession.close(); 20 } 21 22 } 23 }
运行结果:
测试一级缓存:Employee [id=null, lastName=joy33333, email=joy52112225@iclound.com, gender=女]
测试一级缓存:Employee [id=null, lastName=joy33333, email=joy52112225@iclound.com, gender=女],true
运行结果说明:
employee ==employee1 结果为true说明两个地址值相同,说明是同一个employee;
控制台日志显示查询的SQL语句只执行了一次,说明SQL只执行了一次;
一级缓存失效的情况(没有使用到当前一级缓存的情况:效果就是,还需要像数据库发请求)
1.只要sqlSession不同
2.sqlSession相同,查询条件不同;原因是当前一级缓存中还没有该数据;
3.sqlSession相同,两次查询中执行了 增删改 操作也会失效;原因是增删改操作可能会影响数据。
4.qlSession相同 但是手动清空了一级缓存
验证实例(实时使用上一篇的目录结构):
接口文件EmployeeMapper.java:
同上
映射文件EmployeeMapper.xml
同上
junit测试类:
1 @Test 2 public void test02() { 3 String resource = "mybatis-config.xml"; 4 SqlSession openSession = null; 5 SqlSession openSession2 = null; 6 try { 7 InputStream inputStream = Resources.getResourceAsStream(resource); 8 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 9 // 获取openSession 不会自动提交数据 10 openSession = sqlSessionFactory.openSession(true); 11 openSession2 = sqlSessionFactory.openSession(true); 12 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); 13 Employee employee = mapper.getEmpById(1); 14 Employee employee1 = mapper.getEmpById(1); 15 System.out.println("测试一级缓存:" + employee); 16 System.out.println("测试一级缓存:" + employee1 + "," + (employee == employee1)); 17 18 EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); 19 Employee employee2 = mapper2.getEmpById(1); 20 System.out.println("sqlSession改变 一级缓存失效:" + employee2 + "," + (employee1 == employee2)); 21 EmployeeMapper mapper23 = openSession2.getMapper(EmployeeMapper.class); 22 Employee employee23 = mapper23.getEmpById(2); 23 System.out.println("查询条件改变改变 一级缓存失效:" + employee23 + "," + (employee1 == employee23)); 24 25 Employee emp = new Employee(); 26 emp.setLastName("Tom"); 27 emp.setGender("男"); 28 emp.setEmail("678@qq.com"); 29 mapper23.addEmp(emp); 30 Employee employee4 = mapper23.getEmpById(2); 31 System.out.println("查询条件相同+sqlSession相同 一级缓存失效:" + employee23 + "," + (employee4 == employee23)); 32 33 Employee employee5 = mapper23.getEmpById(2); 34 openSession2.clearCache(); 35 Employee employee6 = mapper23.getEmpById(2); 36 System.out.println("手动清楚缓存:" + (employee5 == employee6)); 37 } catch (Exception e) { 38 // TODO: handle exception 39 } finally { 40 if (openSession != null) { 41 openSession.close(); 42 } 43 44 } 45 }
运行结果:如图:
二级缓存:(全局缓存) 基于namespace级别的一个缓存:
一个namespace对应一个二级缓存;
工作机制:
1.一个会话,查询一条数据,这个数据会被放在一级缓存中;
2.如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新会话查询信息,就可以参照二级缓存中的内容
3.sqlSession EmployeeMapper Employee DepartmentMapper Department
不同namespace查出的数据会放在自己对应的缓存(map)中
效果,数据会从二级缓存中取,查出的数据都会放在一级缓存中; 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中;
使用步骤:
1.开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
2.去mapper.xml中配置使用二级缓存;
3.我们的POJO需要实现序列化接口;
验证实例(实时使用上一篇的目录结构):
全局文件mybatis-config.xml:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <properties resource="dbconfig.properties"></properties> 7 8 <settings> 9 <setting name="cacheEnabled" value="true" /> 10 </settings> 11 12 <environments default="development"> 13 <environment id="development"> 14 <transactionManager type="JDBC" /> 15 <dataSource type="POOLED"> 16 <property name="driver" value="${jdbc.driver}" /> 17 <property name="url" value="${jdbc.url}" /> 18 <property name="username" value="${jdbc.username}" /> 19 <property name="password" value="${jdbc.password}" /> 20 </dataSource> 21 </environment> 22 <environment id="development_mysql2"> 23 <transactionManager type="JDBC" /> 24 <dataSource type="POOLED"> 25 <property name="driver" value="${jdbc2.driver}" /> 26 <property name="url" value="${jdbc2.url}" /> 27 <property name="username" value="${jdbc2.username}" /> 28 <property name="password" value="${jdbc2.password}" /> 29 </dataSource> 30 </environment> 31 </environments> 32 33 <databaseIdProvider type="DB_VENDOR"> 34 <!-- 为不同的数据库厂商取别名 --> 35 <property name="MySQL" value="mysql" /> 36 <property name="Oracle" value="oracle" /> 37 <property name="SQL Server" value="sqlserver" /> 38 </databaseIdProvider> 39 <mappers> 40 <package name="dao" /> 41 </mappers> 42 </configuration>
映射文件EmployeeMapper.xml:使用二级缓存<cache></cache>
cache 标签的属性说明:
- eviction:缓存回收策略;
- flushInterval:缓存刷新间隔,多长时间清空一次,默认不清空
- readOnly:缓存只读;true: mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据;
- mybatis为了加快速度,直接把数据在缓存中的引用交给用户;不安全;
- false:非只读;mybatis局的数据可能会被修改。mybatis会利用序列化和反序列化的技术克隆一份新数据给用户;安全,速度慢
- size:存放多少元素;
- type:指定自定义缓存的全类名;实现cache接口即可;
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="dao.EmployeeMapper"> 6 <cache></cache> 18 <insert id="addEmp" parameterType="entity.Employee" useGeneratedKeys="true" keyProperty="id"> 19 insert into tbl_employee(last_name,gender,email) values( 20 #{lastName},#{gender},#{email} 21 ) 22 </insert> 23 24 <select id="getEmpById" parameterType="Integer" resultType="entity.Employee"> 25 select LAST_NAME AS 26 lastName,gender as gender,email as email from 27 tbl_employee where id =#{asdsdfsdf} 28 </select> 29 30 </mapper>
Employee.java实体类实现序列化接口:
1 package entity; 2 3 import java.io.Serializable; 4 5 public class Employee implements Serializable { 6 7 /** 8 * 9 */ 10 private static final long serialVersionUID = -6962919367201266002L; 11 private Integer id; 12 private String lastName; 13 private String email; 14 private String gender; 15 private Department department; 16 17 public Integer getId() { 18 return id; 19 } 20 21 public void setId(Integer id) { 22 this.id = id; 23 } 24 25 public String getLastName() { 26 return lastName; 27 } 28 29 public void setLastName(String lastName) { 30 this.lastName = lastName; 31 } 32 33 public String getEmail() { 34 return email; 35 } 36 37 public void setEmail(String email) { 38 this.email = email; 39 } 40 41 public String getGender() { 42 return gender; 43 } 44 45 public void setGender(String gender) { 46 this.gender = gender; 47 } 48 49 public Department getDepartment() { 50 return department; 51 } 52 53 public void setDepartment(Department department) { 54 this.department = department; 55 } 56 57 @Override 58 public String toString() { 59 return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + "]"; 60 } 61 62 }
接口文件EmployeeMapper.java:
1 void addEmp(Employee employee); 2 Employee getEmpById(int id);
junit测试类:
1 @Test 2 public void test01() { 3 String resource = "mybatis-config.xml"; 4 SqlSession openSession = null; 5 SqlSession openSession2 = null; 6 try { 7 InputStream inputStream = Resources.getResourceAsStream(resource); 8 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 9 // 获取openSession 不会自动提交数据 10 openSession = sqlSessionFactory.openSession(true); 11 openSession2 = sqlSessionFactory.openSession(true); 12 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); 13 EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); 14 Employee employee = mapper.getEmpById(1); 15 16 System.out.println("测试二级缓存:" + employee); 17 openSession.close(); 18 Employee employee1 = mapper2.getEmpById(1); 19 System.out.println("测试二级缓存:" + employee1); 20 openSession2.close(); 21 } catch (Exception e) { 22 // TODO: handle exception 23 } finally { 24 if (openSession != null) { 25 openSession.close(); 26 openSession2.close(); 27 } 28 29 } 30 }
运行结果:
运行结果说明:
Cache Hit Ratio [dao.EmployeeMapper]: 0.5 且没有 发送相同的SQL,说明 :第二次的记录是从缓存Cache Hit Ratio
中取出的;
和缓存有关的设置和属性:
1.cacheEnabled = true; false会关闭二级缓存;不会关闭一级缓存
2.每个select 都有 useCache="true" 若为false 关闭的是二级缓存一级缓存没有关闭
3. 每个增删标签的:flushCache="true" 增删改操作执行完成后就清除缓存 一级缓存失效;二级缓存也会失效
每个select 都有 flushCache="false" 若为true 一级缓存失效;二级缓存也会失效
即:
1 <select id="getEmpById" parameterType="Integer" resultType="entity.Employee" flushCache="true"> 2 select LAST_NAME AS 3 lastName,gender as gender,email as email from 4 tbl_employee where id =#{asdsdfsdf} 5 </select>
运行结果如下图:发送了两次sql;一级缓存失效;二级缓存也会失效;
4. openSession2.clearCache(); 是清空的是一级缓存,不会影响二级缓存
5.localCacheScope:本地缓存作用域(一级缓存session 当前会话的所有数据保存在会话中) statement可以禁用一级缓存