MyBatis学习-5

一级缓存 二级缓存

Posted by lvyonghao on April 18, 2019

MyBatis学习-5

本次学习我将为大家讲解一下MyBatis的缓存,它可以在我们实际开发使用时为我们节省很大的时间,类似JDBC的缓存池。

一级缓存

一级缓存,(本地缓存),sqlSession级别的缓存,一级缓存是一直开起的,与数据库同一次会话期间查询到的数据会放在本地缓存中。以后我们如果需要相同的数据,直接从缓存中拿,没必要去查询数据库; 下面我们通过使用一个sqlSession来获取两次相同的数据看看他们到底是不是同一个内容

   @Test
    public void test09() throws IOException{
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        SqlSession openSession = sqlSessionFactory.openSession();
//        SqlSession openSession2 = sqlSessionFactory.openSession();

        try {
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            Employee emp1 = mapper.getEmpById(1);
//            EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
            System.out.println(emp1);
            //xxx
            Employee emp2 = mapper.getEmpById(1);
            System.out.println(emp2);
            System.out.println(emp1 == emp2);
        }finally {
            openSession.close();
        }
    }

我们发现输出的结果是true说明两次getMappper返回的是同一个数据,再到我们的控制台去看,它说DEBUG [main] - ==> Preparing: select id,last_name Lastname,email,gender from tbl_employee where id = ? DEBUG [main] - ==> Parameters: 1(Integer) DEBUG [main] - <== Total: 1 Employee{id=1, Lastname='lvyonghao', email='snake_lvyonghao@163.com', gender='1', department=null} Employee{id=1, Lastname='lvyonghao', email='snake_lvyonghao@163.com', gender='1', department=null}证明了我们确实只做了一次查询。 下面我们讨论一级缓存失效的情况

  • sqlSession不同 如果我再新建一个sqlSession同样的查询语句他会不会再次从缓存池中拿出来呢?
      @Test
      public void test09() throws IOException{
          SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    
          SqlSession openSession = sqlSessionFactory.openSession();
          SqlSession openSession2 = sqlSessionFactory.openSession();
    
          try {
              EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
              Employee emp1 = mapper.getEmpById(1);
              EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
              System.out.println(emp1);
              //xxx
              Employee emp2 = mapper2.getEmpById(1);
              System.out.println(emp2);
              System.out.println(emp1 == emp2);
          }finally {
              openSession.close();
          }
      }
    

    运行的结果是false,并且我们在控制台中也可以看到它确实发送了两次sql,说明这个时候sqlSession缓存失效了。

  • sqlSession相同,查询条件不同
      @Test
      public void test09() throws IOException{
          SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    
          SqlSession openSession = sqlSessionFactory.openSession();
    //        SqlSession openSession2 = sqlSessionFactory.openSession();
    
          try {
              EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
              Employee emp1 = mapper.getEmpById(1);
    //            EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
              System.out.println(emp1);
              //xxx
              Employee emp2 = mapper.getEmpById(3);
              System.out.println(emp2);
              System.out.println(emp1 == emp2);
          }finally {
              openSession.close();
          }
    

    结果是必然的,false,查询条件不同必然没有启用一级缓存。

  • sqlSession相同,两次查询期间执行了增删改语句
      @Test
      public void test09() throws IOException{
          SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    
          SqlSession openSession = sqlSessionFactory.openSession();
          SqlSession openSession2 = sqlSessionFactory.openSession();
    
          try {
              EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
              Employee emp1 = mapper.getEmpById(1);
              EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
              System.out.println(emp1);
              //xxx
    
              mapper.addEmp(new Employee(null,"huwenhao","hunwenhao@outlook.com","1"));
              System.out.println("数据添加成功");
    
              Employee emp2 = mapper.getEmpById(1);
              System.out.println(emp2);
              System.out.println(emp1 == emp2);
          }finally {
              openSession.close();
          }
      }
    

    结果是false,因为我们的增删改操作可能会对当前数据有影响。

  • sqlSession相同,手动清除了一级缓存 ``` @Test public void test09() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

      SqlSession openSession = sqlSessionFactory.openSession();
      SqlSession openSession2 = sqlSessionFactory.openSession();
    
      try {
          EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
          Employee emp1 = mapper.getEmpById(1);
          EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
          System.out.println(emp1);
          //xxx
    
    
          openSession.clearCache();
    

// mapper.addEmp(new Employee(null,”huwenhao”,”hunwenhao@outlook.com”,”1”)); // System.out.println(“数据添加成功”);

        Employee emp2 = mapper2.getEmpById(1);
        System.out.println(emp2);
        System.out.println(emp1 == emp2);
    }finally {
        openSession.close();
    }
} ``` 当我们把一级缓存清理后,结果是false,这个我就不用多说什么了。 实际上sqlSession级别的缓存其实就是一个Map ---

二级缓存

二级缓存也称为全局缓存,它是基于namespace级别的缓存,对应的就是我们XML的namespace,一个namespace对应一个二级缓存。

  1. 一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存。
  2. 如果会话关闭,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息就可以参照二级缓存中的内容
  3. 不同的namespace查出的数据会放在在自己对应的缓存中(Map)

下面我们就实践一下吧

  1. 首先二级缓存是自动开启的,我们不需要手动开启二级缓存配置,当然你也可以显示的配置设个属性,来到我们MyBatis的配置文件当中的setting标签当中,设置cacheEnabled为true。
  2. 我们去Mapper.xml当中配置二级缓存,我们只需要在文件当中添加<cache></cachae>标签就可以了,当然他也有几个属性我们也来介绍一下:
    • evication:缓存回收策略

      LRU : 最近少使用的,移除长时间不使用的对象(默认) FIFD : 先进先出,按对象进入缓存的顺序移除他们 SOFT :软引用,移除基于垃圾回收器状态和软引用规则的对象。 WEAK : 弱引用,更积极的移除给予垃圾收集器状态和弱引用规则的对象。

    • flushInterval:刷新间隔时间(设置为毫秒数)
    • readOnly:是否只读设置为true和false
    • size :设置缓存中放置多少个元素
    • type=“”:自定义缓存的全类名,实现Cache接口即可
  3. 我们的POJO需要实现序列化接口Serializable
    @Test
    public void test10() throws IOException{
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        SqlSession openSession = sqlSessionFactory.openSession();
        SqlSession openSession2 = sqlSessionFactory.openSession();

        try {
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);

            Employee emp01 = mapper.getEmpById(1);
            System.out.println(emp01);
            openSession.close();

            Employee emp02 = mapper2.getEmpById(1);
            System.out.println(emp02);


        }finally {
            openSession.close();
        }
    }

我们在日志中可以看到这么一句话Cache Hit Ratio [com.snake_lvyonghao.MyBatis.Dao.EmployeeMapper]: 0.5意思就是在缓存中命中,也就意味着我们从二级缓存中找到了。


SSM框架源码