MyBatis-缓存机制
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存。
一级缓存和二级缓存:
一级缓存:(本地缓存):SqlSession级别的缓存,一级缓存是一致开启的,没法关闭。方法之间不共用!
与数据库同一次会话期间查询到的数据放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
二级缓存(全局缓存):
①默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
②二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
③为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。
1.测试一级缓存【默认是开启的本地缓存的】
1 public class EmployeeMapperTest { 2 3 4 public SqlSessionFactory getSqlSessionFactory() throws IOException{ 5 String resource = "mybatis-config.xml"; 6 InputStream inputStream = Resources.getResourceAsStream(resource); 7 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 return sqlSessionFactory; 9 } 10 11 @Test 12 public void testFirstLevelCache() throws Exception{ 13 String resource = "mybatis-config.xml"; 14 InputStream inputStream = Resources.getResourceAsStream(resource); 15 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 16 17 SqlSession sqlSession = sqlSessionFactory.openSession(); 18 19 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); 20 Employee emp = mapper.getEmpById(1); 21 System.out.println(emp); 22 23 EmployeeMapper mapper2 = sqlSession.getMapper(EmployeeMapper.class); 24 Employee emp2 = mapper2.getEmpById(1); 25 26 System.out.println(emp==emp2); 27 sqlSession.close(); 28 29 } 30 }
因此运行结果为true:
DEBUG 12-26 10:18:07,715 ==> Preparing: select id,last_name,gender,email from tbl_emp where id = ? (BaseJdbcLogger.java:145) DEBUG 12-26 10:18:07,781 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145) DEBUG 12-26 10:18:07,809 <== Total: 1 (BaseJdbcLogger.java:145) Employee [id=1, lastName=zhangsan, gender=true, email=zhangsan@163.com] true
emp==emp2比较两个对象,相当于比较他们在堆中的地址值
一级缓存失效的情况【4种】(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询)
1 package com.neuedu.cache; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 6 import org.apache.ibatis.io.Resources; 7 import org.apache.ibatis.session.SqlSession; 8 import org.apache.ibatis.session.SqlSessionFactory; 9 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 10 import org.junit.Test; 11 12 import com.neuedu.entity.Department; 13 import com.neuedu.entity.Employee; 14 import com.neuedu.mapper.EmployeeMapper; 15 16 public class testLevelCache { 17 18 public SqlSessionFactory getSqlSessionFactory() throws IOException{ 19 String resource = "mybatis-config.xml"; 20 InputStream inputStream = Resources.getResourceAsStream(resource); 21 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 22 return sqlSessionFactory; 23 } 24 25 26 27 //一级缓存 缓存是SqlSession对象独有的,每一个SqlSession对象都有自己的缓存空间 28 //1.sqlSession不同。 29 @Test 30 public void testFirstLevelCache() throws Exception{ 31 String resource = "mybatis-config.xml"; 32 InputStream inputStream = Resources.getResourceAsStream(resource); 33 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 34 35 SqlSession sqlSession = sqlSessionFactory.openSession(); 36 37 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); 38 Employee emp = mapper.getEmployeeById(1); 39 System.out.println(emp); 40 41 42 sqlSession=sqlSessionFactory.openSession(); 43 EmployeeMapper mapper2 = sqlSession.getMapper(EmployeeMapper.class); 44 Employee emp2 = mapper2.getEmployeeById(1); 45 System.out.println(emp==emp2); 46 47 sqlSession.close(); 48 49 } 50 51 //2.SqlSession相同,但是查询条件不一样[当前缓存中还没有这个数据] 52 @Test 53 public void testFirstLevelCache2() throws IOException{ 54 String resource = "mybatis-config.xml"; 55 InputStream inputStream = Resources.getResourceAsStream(resource); 56 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 57 58 SqlSession sqlSession = sqlSessionFactory.openSession(); 59 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); 60 Employee emp = mapper.getEmployeeById(2); 61 System.out.println(emp); 62 63 Employee emp2 = mapper.getEmployeeById(3); 64 System.out.println(emp2); 65 System.out.println(emp == emp2); 66 sqlSession.close(); 67 } 68 69 70 71 72 //3.SqlSession相同,但是两次查询之间执行了增删改操作【这次增删改可能对当前数据有影响】 73 @Test 74 public void testFirstLevelCache3() throws IOException{ 75 String resource = "mybatis-config.xml"; 76 InputStream inputStream = Resources.getResourceAsStream(resource); 77 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 78 79 SqlSession sqlSession = sqlSessionFactory.openSession(true); 80 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); 81 82 Employee emp = mapper.getEmployeeById(2); 83 System.out.println(emp); 84 mapper.insertEmployee(new Employee(null, "孙悟空", true, "sunwukong@163.com", new Department(1,"开发部"))); 85 Employee emp2 = mapper.getEmployeeById(2); 86 System.out.println(emp2); 87 System.out.println(emp == emp2); 88 sqlSession.close(); 89 90 } 91 92 93 //4.SqlSession相同,手动清除了一级缓存[缓存清空]。 94 @Test 95 public void testGetEmployee() throws IOException{ 96 String resource = "mybatis-config.xml"; 97 InputStream inputStream = Resources.getResourceAsStream(resource); 98 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 99 100 SqlSession sqlSession = sqlSessionFactory.openSession(true); 101 EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); 102 Employee emp = mapper.getEmployeeById(2); 103 System.out.println(emp); 104 //手动清空缓存 105 sqlSession.clearCache(); 106 Employee emp2 = mapper.getEmployeeById(2); 107 System.out.println(emp2); 108 System.out.println(emp == emp2); 109 sqlSession.close(); 110 } 111 }
运行结果:
1 DEBUG 12-26 10:16:30,839 Cache Hit Ratio [com.neuedu.mapper.EmployeeMapper]: 0.0 (LoggingCache.java:62) 2 DEBUG 12-26 10:16:31,271 ==> Preparing: select * from employee where id=? (BaseJdbcLogger.java:145) 3 DEBUG 12-26 10:16:31,334 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145) 4 DEBUG 12-26 10:16:31,360 <== Total: 1 (BaseJdbcLogger.java:145) 5 Employee [id=1, username=张三丰, gender=true, email=zhngsanfeng@163.com, dept=null] 6 DEBUG 12-26 10:16:31,361 Cache Hit Ratio [com.neuedu.mapper.EmployeeMapper]: 0.0 (LoggingCache.java:62) 7 DEBUG 12-26 10:16:31,457 ==> Preparing: select * from employee where id=? (BaseJdbcLogger.java:145) 8 DEBUG 12-26 10:16:31,458 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145) 9 DEBUG 12-26 10:16:31,461 <== Total: 1 (BaseJdbcLogger.java:145) 10 false
都是执行两次sql语句
2.二级缓存:【全局缓存】:基于namespace级别的缓存:一个namespace对应一个二级缓存。
【一级缓存的范围还是太小了,每次SqlSession一关闭,一级缓存中的数据就消失】
所以从这个角度讲:能跨sqlSession的缓存为二级缓存!
mybatis-config.xml:
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
EmployeeMapper.xml:
<!--
二级缓存是namespace级别的,一个namespace【mapper接口】对应一个二级缓存
-->
<cache></cache>
实体类还需要实现Serializable接口:
public class Employee implements Serializable{
}
//二级缓存 @Test public void testLevelCache() throws Exception{ String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmployeeById(1); System.out.println(emp); sqlSession.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class); Employee emp2 = mapper2.getEmployeeById(1); System.out.println(emp==emp2); sqlSession.close(); }
运行结果:
DEBUG 12-26 10:26:31,261 Cache Hit Ratio [com.neuedu.mapper.EmployeeMapper]: 0.0 (LoggingCache.java:62) DEBUG 12-26 10:26:31,731 ==> Preparing: select * from employee where id=? (BaseJdbcLogger.java:145) DEBUG 12-26 10:26:31,800 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145) DEBUG 12-26 10:26:31,830 <== Total: 1 (BaseJdbcLogger.java:145) Employee [id=1, username=张三丰, gender=true, email=zhngsanfeng@163.com, dept=null] DEBUG 12-26 10:26:31,844 Cache Hit Ratio [com.neuedu.mapper.EmployeeMapper]: 0.5 (LoggingCache.java:62) false
执行一次sql语句