MyBatis(六)缓存机制 之 一级缓存

一、一级缓存介绍

  1、一级缓存(local cache),即本地缓存,作用域默认为 SqlSession。当 Session flush 或 close 后,该 session 中的所有 Cache 将被清空。

  2、本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存,或者改变缓存的作用域。

  3、在 MyBatis3.1 之后,可以配置本地缓存的作用域,在 MyBatis.xml 中配置。

  4、一级缓存是 sqlsession 级别的缓存。一级缓存是一直开启的,sqlsession级别的一个 map,与数据库同一次会话期间查询到的数据会放在本地缓存中。

    多个一级缓存中的数据不能共用。

    以后如果需要获取相同的数据,直接从缓存中获取,不必再去查询数据库。

  5、一级缓存的工作机制

    同一次会话期间只要查询过的数据都会保存在当前的 SqlSession 的一个 Map 中

    key:hashCode+查询的 SqlId + 编写的 sql 查询语句 + 参数

二、一级缓存失效的四种情况

  一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据发送SQL)

  1、sqlSession 不同:使用不同的 sqlSession 数据库会话,不同的 SqlSession 对应不同的一级缓存;

  2、sqlSession 相同:但查询条件不同(当前一级缓存中还没有这个数据)

  3、如果sqlSession相同:两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)

  4、如果sqlSession相同,手动清除了一级缓存(把缓存内容清空)

  SqlSession 级别的缓存就相当于一个 Map。

三、一级缓存演示

  1、在同一个 SqlSession 中查询同一条记录

    代码:

    /**
      * mybatis中的一级缓存默认开启,是SqlSession级别的
      * 即同一个SqlSession对于一个sql语句,执行之后就会存储在缓存中,
      * 下次执行相同的sql,直接从缓存中取
      */
      @Test
      public void testFirstLevelCache() throws IOException {
           SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
           SqlSession sqlSession = sqlSessionFactory.openSession();
           try {
                EmployeeMapperCache mapper = sqlSession.getMapper(EmployeeMapperCache.class);
                Employee emp01 = mapper.getEmpById(1);
                System.out.println("emp01 = " + emp01);

                //处理业务
                Employee emp02 = mapper.getEmpById(1);
                System.out.println("emp02 = " + emp02);
                System.out.println(emp01 == emp02);

           } finally {
                sqlSession.close();
           }
      }

    运行结果:

       可以看出,查询的是同一条记录,但第一次是从数据库中查询的,有 SQL 语句,第二次查询是就是从 缓存(SqlSession)中获取的。

  2、从不同的 SqlSession 中查询同一条记录

    代码:

    /**
      * 失效情况1:从不同的 SqlSession 中查询同一条记录
      * @throws IOException
      */
     @Test
     public void testFirstLevelCacheInvalid1() throws IOException {
          SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
          SqlSession sqlSession = sqlSessionFactory.openSession();
          try {
               EmployeeMapperCache mapper = sqlSession.getMapper(EmployeeMapperCache.class);
               Employee emp01 = mapper.getEmpById(1);
               System.out.println("emp01 = " + emp01);

               //处理业务
               SqlSession sqlSession2 = sqlSessionFactory.openSession();
               EmployeeMapperCache mapper2 = sqlSession2.getMapper(EmployeeMapperCache.class);
               Employee emp02 = mapper2.getEmpById(1);
               System.out.println("emp02 = " + emp02);

               System.out.println(emp01 == emp02);

          } finally {
               sqlSession.close();
          }
     }

    运行结果:

     虽然查询的是同一条记录,但是从不同的 SqlSession 中获取的,都从数据库进行查询,并没有从缓存中获取。

  3、sqlSession 相同,但查询条件不同

    代码:

    /**
      * 失效情况2:sqlSession 相同:但查询条件不同
      * @throws IOException
      */
     @Test
     public void testFirstLevelCacheInvalid2() throws IOException {
          SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
          SqlSession sqlSession = sqlSessionFactory.openSession();
          try {
               EmployeeMapperCache mapper = sqlSession.getMapper(EmployeeMapperCache.class);
               Employee emp01 = mapper.getEmpById(1);
               System.out.println("emp01 = " + emp01);

               //处理业务
               Employee emp02 = mapper.getEmpById(3);
               System.out.println("emp02 = " + emp02);

               System.out.println(emp01 == emp02);
          } finally {
               sqlSession.close();
          }
     }

    运行结果:

     使用的是同一个 sqlSession 对象,但是查询条件不同,也没有用到缓存,因为缓存中就没有符合此查询条件的数据。

  4、在通过一个 SqlSession 中,两次查询之间有 增删改操作

        代码:

    /**
      * 失效情况3:在通过一个 SqlSession 中,两次查询之间有 增删改操作
      * @throws IOException
      */
     @Test
     public void testFirstLevelCacheInvalid3() throws IOException {
          SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
          SqlSession sqlSession = sqlSessionFactory.openSession();
          try {
               EmployeeMapperCache mapper = sqlSession.getMapper(EmployeeMapperCache.class);
               Employee emp01 = mapper.getEmpById(1);
               System.out.println("emp01 = " + emp01);

               //处理业务
               mapper.updateEmp(new Employee(3, "John", "1", "John@163.com"));
               System.out.println("更新成功");

               Employee emp02 = mapper.getEmpById(1);
               System.out.println("emp02 = " + emp02);

               System.out.println(emp01 == emp02);
          } finally {
               sqlSession.close();
          }
     }

    运行结果:

   还是从同一个 SqlSession 中查询同一条记录,但由于期间涉及了增删改操作,一级缓存就会失效。

  (因为有可能修改的就是 查询的记录,这时再返回,就与数据库中记录不一致)

  5、在同一个 SqlSession 中,手动清空缓存

    代码:

    /**
      * 失效情况4:在同一个 SqlSession 中,手动清空缓存
      * @throws IOException
      */
     @Test
     public void testFirstLevelCacheInvalid4() throws IOException {
          SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
          SqlSession sqlSession = sqlSessionFactory.openSession();
          try {
               EmployeeMapperCache mapper = sqlSession.getMapper(EmployeeMapperCache.class);
               Employee emp01 = mapper.getEmpById(1);
               System.out.println("emp01 = " + emp01);

               //处理业务
               sqlSession.clearCache();

               Employee emp02 = mapper.getEmpById(1);
               System.out.println("emp02 = " + emp02);

               System.out.println(emp01 == emp02);
          } finally {
               sqlSession.close();
          }
     }

    运行结果:

   虽然是在同一个 SqlSession 中进行查询的同一条记录,但由于在两次查询期间清空了缓存,还需要重新从数据库查询。

原文地址:https://www.cnblogs.com/niujifei/p/15243662.html