MyBatis(十三)——MyBatis中的缓存问题

x33g5p2x  于2021-09-29 转载在 其他  
字(3.4k)|赞(0)|评价(0)|浏览(546)
前言

缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。mybatis同样也提供了缓存机制。

mybatis的缓存分为两级:一级缓存、二级缓存

默认情况下,只有一级缓存开启,一级缓存是SqlSession级别的缓存,缓存的数据只在SqlSession内有效。
*
二级缓存需要手动开启和配置,二级缓存是(namespace)mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的。
*
为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存。

一、一级缓存

这个缓存系统默认情况下是开启的,当我们获取到一个SqlSession对象之后,如果调用SqlSession中的同一个方法查询同一条数据,那么第二次查询将不会去数据库中查询,因为第一次查询有缓存,直接调用缓存数据即可,除非缓存超时或者我们明确声明数据要刷新,否则都是直接调用缓存数据。

测试如下:

@Test
public void testGetStudentById() {
    SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);
    //查询同一条数据时会缓存
    Student student = mapper.getStudnetById(38);
    Student student1 = mapper.getStudnetById(38);
    System.out.println(student);
    System.out.println(student1);
    sqlSession.commit();
    sqlSession.close();
}

效果如下:

可以看到:我这里执行了两次查询,但实际上只执行了一次SQL语句。

注意事项:

1、如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前SqlSession缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读。
2、我们也可以手动清理缓存

sqlSession.clearCache();
二、二级缓存

上面的缓存是由系统默认配置的,这个有一定的局限性,就是只能在同一个SqlSession中有效,脱离了同一个SqlSession就没法使用这个缓存了,一级缓存作用域太低了,有的时候我们可能希望能够跨SqlSession进行数据缓存。那么这个时候需要我们进行手动开启二级缓存。

二级缓存是mapper级别的缓存,也就是同一个namespace的mappe.xml,当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个二级缓存区域。

我们做下测试,首先不开启二级缓存,使用不同的SqlSession操作同一个Mapper下面的同一个查询语句:

@Test
public void testGetStudentById() {
    SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);
    Student student = mapper.getStudnetById(38);
    System.out.println(student);
    sqlSession.commit();
    sqlSession.close();
    SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
    StudentDao mapper1 = sqlSession1.getMapper(StudentDao.class);
    Student student1 = mapper1.getStudnetById(38);
    System.out.println(student1);
    sqlSession1.commit();
    sqlSession1.close();
}

效果如下:

可以看到,没有使用到缓存。

下面我们开启二级缓存:

1、全局显示开启(默认,不添加也可以):

<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>

2、在Mapper.xml中使用缓存,只需要我们在userMapper.xml中配置cache节点即可:

<mapper namespace="com.macay.dao.StudentDao">
    <cache/>

select语句都会被缓存,所有的delete、insert和update则都会将缓存刷新,还比如缓存将使用LRU算法进行内存回收等。那么这些东西如果需要配置的话,我们可以按如下方式进行配置:

<cache eviction="LRU" flushInterval="20000" size="1024" readOnly="true"/>

这里的eviction表示缓存策略,除了LRU之外还有先进先出(FIFO)、软引用(SOFT)、弱引用(WEAK)等,flushInterval则表示刷新时间,表示缓存的对象个数,readOnly为true则表示缓存只可以读取不可以修改。

做了如上配置之后还不够,开启二级缓存还要求我们的实体类可以序列化,实现Serializable接口即可,如下:

ublic class Student implements Serializable {
    private Integer id;
    private String name;
    private String email;
    private Integer age;
    private Boolean partyMember;
    private Classess cla;
    private List<Integer> hobbies;
    private GenderEnum gender;
    private Date regDate;

测试一下:

@Test
public void testGetStudentById() {
    SqlSession sqlSession = SqlSessionUtils.getSqlSession();
    StudentDao mapper = sqlSession.getMapper(StudentDao.class);
    Student student = mapper.getStudnetById(38);
    System.out.println(student);
    sqlSession.commit();
    sqlSession.close();
    SqlSession sqlSession1 = SqlSessionUtils.getSqlSession();
    StudentDao mapper1 = sqlSession1.getMapper(StudentDao.class);
    Student student1 = mapper1.getStudnetById(38);
    System.out.println(student1);
    sqlSession1.commit();
    sqlSession1.close();
}

可以看到SQL语句实际上只执行了一次。

具体流程:

1.当一个sqlseesion执行了一次select后,在关闭此session的时候,会将查询结果缓存到二级缓存

2.当另一个sqlsession执行select时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能。
      
注意事项:

1、如果SqlSession执行了DML操作(insert、update、delete),并commit了,那么mybatis就会清空当前mapper缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现脏读。

相关文章