MyBatis缓存

文章目录

  • 前言
  • 一、一级缓存
    • 1、一级缓存默认开启
    • 2、一次缓存失效的四种情况
      • 情况1 —— sqlSession不同
      • 情况2 —— sqlSession相同,查询条件不同
      • 情况3 —— sqlSession相同,两次查询之间执行了增删改操作
      • 情况4 —— sqlSession相同,手动清除了一级缓存(缓存清空)
  • 二、二级缓存
    • 1、工作机制
    • 2、使用步骤
    • 3、和缓存有关的属性(设置)
      • (1). 全局配置文件中的``中设置cacheEnabled=true / false 开启/关闭缓存(注意:二级缓存,一级缓存一直可用)
      • (2). 每个``标签都有useCache=true /false 使用/不使用缓存(注意:false表示一级缓存依然使用,二级缓存不使用)。
      • (3). 每个增删改标签的:flushCache=true(一二级缓存都会清除),增删改执行完成后就会清空缓存
      • (4). 查询标签的flushCache默认为false,若设置为true,每次查询之前会清空一级和二级缓存 ,即缓存是没有使用的
      • (5). sqlSession.clearCache()只是清空当前session的一级缓存
      • (6). 全局``中,可以设置name=localCacheScope :本地缓存作用域(一级缓存session),value=SESSION:当前会话的所有数据保存在会话缓存中;value=STATEMENT:可以禁用一级缓存
  • 三、缓存原理图示


前言

MyBatis系统默认定义了两级缓存,分别是一级缓存和二级缓存。

  1. 一级缓存又称本地缓存,sqlSession级别的缓存,是默认开启
  2. 二级缓存又称全局缓存,namespace级别的缓存(一个namespace对应一个二级缓存),需要手动开启和配置
  3. 为了提高扩展性。 MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存

一、一级缓存

  • 一级缓存(local cache),即本地缓存,作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。
  • 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.
    测试使用数据表tbl_employee部分数据如下:
    MyBatis缓存_第1张图片

1、一级缓存默认开启

	@Test
    public void test01() throws IOException {
        SqlSession sqlSession = getSqlSessionFactory().openSession();
        try {
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee emp1 = mapper.getEmpById(2);
            System.out.println(emp1);
            Employee emp2 = mapper.getEmpById(2);
            System.out.println(emp2);
            System.out.println(emp1 == emp2);
        }finally {
            sqlSession.close();
        }
    }
DEBUG 04-07 20:46:55,077 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:46:55,094 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:46:55,110 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}
true

可以看到,进行两次查询,只发送了一次sql,并且两次查询的对象是同一个。即与数据库同一次会话期间查询到的数据会放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

2、一次缓存失效的四种情况

情况1 —— sqlSession不同

	@Test
    public void test02() throws IOException {
        SqlSession sqlSession1 = getSqlSessionFactory().openSession();
        SqlSession sqlSession2 = getSqlSessionFactory().openSession();
        try {
            EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class);
            EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);
            Employee emp1 = mapper1.getEmpById(2);
            System.out.println(emp1);
            sqlSession1.close(); // 会话关闭
            Employee emp2 = mapper2.getEmpById(2);
            System.out.println(emp2);
            sqlSession2.close();
        }finally {

        }
    }
DEBUG 04-07 20:55:45,406 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:55:45,425 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:55:45,438 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}
DEBUG 04-07 20:55:45,451 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:55:45,451 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:55:45,452 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}

情况2 —— sqlSession相同,查询条件不同

@Test
public void test01() throws IOException {
        SqlSession sqlSession = getSqlSessionFactory().openSession();
        try {
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee emp1 = mapper.getEmpById(2);
            System.out.println(emp1);
            Employee emp2 = mapper.getEmpById(4);
            System.out.println(emp2);
            System.out.println(emp1 == emp2);
        }finally {
            sqlSession.close();
        }
    }
DEBUG 04-07 20:57:47,064 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:57:47,078 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:57:47,092 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}
DEBUG 04-07 20:57:47,093 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:57:47,093 ==> Parameters: 4(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 20:57:47,094 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=4, lastName='alan', gender='1', email='jerry@qq.com', dept=null}
false

分别查询2号和4号employee,发送了两次sql,因为第二次查询时当前一级缓存还没有4号employee数据

情况3 —— sqlSession相同,两次查询之间执行了增删改操作

	@Test
    public void test03() throws IOException {
        SqlSession sqlSession = getSqlSessionFactory().openSession();
        try {
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee emp1 = mapper.getEmpById(2);
            System.out.println(emp1);
            // 添加一条记录
            mapper.addEmp(new Employee(3,"ray", "1", "[email protected]", null));
            Employee emp2 = mapper.getEmpById(2);
            System.out.println(emp2);
            System.out.println(emp1 == emp2);
        }finally {
            sqlSession.close();
        }
    }
DEBUG 04-07 21:36:14,924 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:36:14,937 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:36:14,951 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}
DEBUG 04-07 21:36:14,951 ==>  Preparing: insert into tbl_employee(last_name, gender, email) values(?, ?, ?)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:36:14,951 ==> Parameters: ray(String), 1(String), ray@qq.com(String)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:36:14,952 <==    Updates: 1  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:36:14,953 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:36:14,953 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:36:14,953 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}
false

在查询ID为2和4的employee之间,插入了一条ID为3的记录,导致缓存失效,原因在于这次添加可能会对当前数据有影响(删改类似)

情况4 —— sqlSession相同,手动清除了一级缓存(缓存清空)

	@Test
    public void test04() throws IOException {
        SqlSession sqlSession = getSqlSessionFactory().openSession();
        try {
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee emp1 = mapper.getEmpById(2);
            System.out.println(emp1);
            sqlSession.clearCache();
            Employee emp2 = mapper.getEmpById(2);
            System.out.println(emp2);
            System.out.println(emp1 == emp2);
        }finally {
            sqlSession.close();
        }
    }
DEBUG 04-07 21:42:38,146 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:42:38,162 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:42:38,176 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}
DEBUG 04-07 21:42:38,176 ==>  Preparing: select * from tbl_employee where id=?  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:42:38,176 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 04-07 21:42:38,177 <==      Total: 1  (BaseJdbcLogger.java:137) 
Employee{id=2, lastName='Mary', gender='1', email='Mary@google.com', dept=null}
false

二、二级缓存

  1. 二级缓存(second level cache),全局作用域缓存
  2. 二级缓存默认不开启,需要手动配置
  3. 二级缓存在 sqlSession 关闭或提交之后才会生效

1、工作机制

  1. 一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中

  2. 如果会话关闭,一级缓存的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容

  3. 不同的namespace查出的数据会放在自己对应的缓存中

总结:查出的数据都会默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中

2、使用步骤

  1. 开启全局二级缓存配置
<setting name="cacheEnabled" value="true"/>`
  1. 去xxxMapper.xml中配置使用二级缓存
 <cache type="org.mybatis.caches.ehcache.EhcacheCache">cache>
  1. 在pojo中实现序列化接口
public class Employee implements Serializable {
    private Integer id;
    private String lastName;
    private String gender;
    private String email;
    private Department dept;
}

3、和缓存有关的属性(设置)

(1). 全局配置文件中的中设置cacheEnabled=true / false 开启/关闭缓存(注意:二级缓存,一级缓存一直可用)

(2). 每个