mybatis查询缓存之一级缓存和二级缓存详细解析

一:本文将涉及到的如下几方面的知识点

  1. mybatis基本增删改查
  2. Java序列化和反序列化
  3. JDBC和sqlsession基本理论
  4. spring事务管理

二:mybatis查询缓存基本介绍

  1. 缓存:将相同查询条件的sql语句执行一遍后所得到的结果存在内存或者某种缓存介质当中,当下次遇到一模一样的查询sql时候不在执行sql与数据库交互,而是直接从缓存中获取结果,减少服务器的压力;
  2. mybatis的查询缓存又分为一级缓存和二级缓存,一级缓存的作用范围为同一个sqlsession,而二级缓存的作用范围为同一个namespace和mapper;这是接下来的篇幅重点介绍的;

三:mybatis缓存详细介绍

     1:一级缓存

       一级缓存是mybatis默认就帮我们开启的,我们不需要多做配置,但是我们得知道其中原理,否则我们也不知道怎么使用,也不知道我们到底有没有一级缓存。

       上面第二部分说过一级缓存的作用域是同一个sqlsession,sqlsession的作用就是建立和数据库的会话,我们对数据库表的增删改查都是通过sqlsession去执行指定的sql完成的,而sqlsession和数据库的连接并不是永久连接的,也一定要杜绝这种永久连接;所以就有了sqlsession的创建和关闭,sqlsession默认执行完一段的sql片段后就会close掉sqlsession,即销毁sqlsession;而下一次对数据库的操作的又会重新建立会话关系,即建立新的sqlsession,所以这就和前一次的执行sql的sqlsession属于不同的SQL session了,也就这两个sqlsession就不存在一级缓存关系了;代码分析如下:

public CourseDto getCourse(String courseId) {
    log.info("测试mybatis默认开启的一级缓存");
    log.info("第一次查询");
    Course course = courseDao.getSqlSession().selectOne("listCourseByAutoMapping", courseId);
    log.info("第二次查询");
    Course course1 = courseDao.getSqlSession().selectOne("listCourseByAutoMapping", courseId);
    CourseDto courseDto = mapper.map(course, CourseDto.class);
    return courseDto;
}

       我们说过一级缓存不需要任何配置,mybatis默认开启的,所以这里直接写service,没有其他额外配置;代码内容是对同一个sql代码片段连续调用两次,控制台日志打印如下:

mybatis查询缓存之一级缓存和二级缓存详细解析_第1张图片                                                                           图1-1:未正确使用一级缓存

 

mybatis查询缓存之一级缓存和二级缓存详细解析_第2张图片                                                                               图1-2:未正确使用一级缓存

       由两张图可以看出上面的代码执行了两次与数据库的会话,因为每次都新建了sqlsession,不在一级缓存的作用域内,而且每次执行的sql也都帮我们打印出来了,所以说程序并没有用到我们的一级缓存;那么如何将一级缓存利用起来呢?这就需要用到我们的spring事务管理;现在的代码如下:

@Transactional(propagation = Propagation.REQUIRES_NEW)//开启spring的申明事务
@Override
public CourseDto getCourse(String courseId) {
    log.info("测试mybatis默认开启的一级缓存");
    log.info("第一次查询");
    Course course = courseDao.getSqlSession().selectOne("listCourseByAutoMapping", courseId);
    log.info("第二次查询");
    Course course1 = courseDao.getSqlSession().selectOne("listCourseByAutoMapping", courseId);
    log.info("第二次执行查询完毕");
    CourseDto courseDto = mapper.map(course, CourseDto.class);
    return courseDto;
}

    执行结果如下图:

mybatis查询缓存之一级缓存和二级缓存详细解析_第3张图片                                                                            图2:正确使用一级缓存结果

      可以看到开启spring事务之后,同样的业务代码,在这里sql片段只打印一遍,而且在执行第二次查询的时候没有在重新创建sqlsession,所以是同一个sqlsession,说明第二次查询的数据是从缓存中获取的,所以证明这里是一级缓存起了作用;关于spring事务不知如何使用的,可以参看我的另一篇文章(https://blog.csdn.net/llf_1241352445/article/details/79831031);而关于spring事务管理的service只会一个创建sqlsession,这是因为事务管理下的sql执行方式是BATCH,只会与数据库交互一次,一次执行完所有的sql,所以只会创建一个sqlsession;

       2:二级缓存

       二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于SQL session,所以这里,我们需要对mybatis的配置修改,开启二级缓存设施,而且需要在我们的namespace下开启缓存,具体如下

     在mybatis-config.xml文件中的标签配置开启缓存,代码如下:

     

          

    单单配置这个还是不够的,还需要在我们的mapper的xml文件下开启缓存,即加入该标签:

 
 

然后可以开始试着运行我们的代码如下:   

@Override
public CourseDto getCourse(String courseId) {
    log.info("测试mybatis默认开启的二级缓存");
    Course course = courseDao.getSqlSession().selectOne("listCourseByAutoMapping", courseId);
    log.info("第一次查询结果:{}",course.toString());
    Course course1 = courseDao.getSqlSession().selectOne("listCourseByAutoMapping", courseId);
    log.info("第二次执行查询完毕:{}",course1.toString());
    CourseDto courseDto = mapper.map(course, CourseDto.class);

    return courseDto;
}

执行结果如下:

mybatis查询缓存之一级缓存和二级缓存详细解析_第4张图片                                                                             图三:序列化异常

 

        执行结果报序列化异常,那是因为我们映射的pojo对象未实现序列化接口,说明我们从缓存数据中读取数据需要进行反序列化,这是因 为mybatis的二级缓存的缓存介质有多种多样,而并不一定是在内存中,所以需要我们对pojo对象进行序列化,只要实现序列化接口即可,而对于序列化和反序列化不清楚的读者可以参看我的另外一篇文章(https://blog.csdn.net/llf_1241352445/article/details/81135882),pojo序列化的就不截图了,直接看我们的正确执行结果,如下图:

mybatis查询缓存之一级缓存和二级缓存详细解析_第5张图片                                                                              图四:二级缓存效果

 

可以看到,第一次查询有打印sql,而第二次查询没有打印sql,而且还有正确的返回数据,而上面的代码也没有开启spring事务

,说明不存在mybatis的一级缓存,唯一能起作用的就是二级缓存了;

       3:拓展延申

       对于同一个service被同一个请求分多次调用,则所有的这些调用之间的一级缓存和二级缓存个事什么情况呢?

      一级缓存是基于sqlsession的,当一次请求结束之后,意味着session执行结束,所以sqlsession也就关闭了,所以缓存也就清空了,因此,对于多次的请求,肯定不存在一级缓存;

     二级缓存是对于namespace和mapper的,只要相同的查询sql,都会优先从缓存区域查找,所以对于多次请求,对于二级缓存是没有影响的。这个大家可以去自己验证下,只要连续发两次的请求验证sql的打印次数就可以得到结论;

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(缓存,一级缓存,二级缓存,sqlsession,事务,mybatis)