MyBatis 一流缓存避坑

图片 1

package org.apache.ibatis.session;/** * @author Eduardo Macarron */public enum LocalCacheScope {  SESSION,  //session 级别的缓存
  STATEMENT //statement 级别的缓存}
实验1

测量检验二级缓存效果,不交付业务,sqlSession1查询完数据后,sqlSession2一模二样的询问是或不是会从缓存中获取数据。

@Test
public void testCacheWithoutCommitOrClose() throws Exception {
        SqlSession sqlSession1 = factory.openSession(true); 
        SqlSession sqlSession2 = factory.openSession(true); 

        StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
        StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);

        System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));
}

实施结果:

图片 2

大家得以看出,当sqlsession未有调用commit(卡塔尔(قطر‎方法时,二级缓存并不曾起到作用。

看图中唤醒的推行皮秒数数据库间接询问:12ms数据库直接询问NoTrack:28ms,也会有一点怪,12ms才健康,样板非常不够啊List缓存查询:3msLocal中查询:112ms,注意本身事前曾经将数据总体加载到Local:context.ass_ba_item.Where(c=c.EntId==userModel.EntIdState of Qatar.ToList(卡塔尔;本来想用Local缓存以增加品质,结果尼玛比数据库查询还慢10倍。有掌握原理的大拿,麻烦解释下,多谢。

图片 3

二级缓存

证清代码:

正文已授权Gitchat独家公布,未经Gitchat许可,不得转发。

自家,后端Java程序猿,今后美团点评职业。
爱强健体魄,爱技能,也心仪写点文字。
个体网址: http://kailuncen.me
公众号: KailunTalk (凯伦说)

能够看出,纵然是千人一面包车型地铁询问,每便查询都以直接读取数据库了。

目录

为完结以上多个目的,本文依照以下依次实行。

  1. Mybatis的底工概念。
  2. 顶级缓存介绍及有关配置。
  3. 一流缓存职业流程及源码剖判。
  4. 一级缓存总计。
  5. 二级缓存介绍及连锁安顿。
  6. 二级缓存源码解析。
  7. 二级缓存总计。
  8. 全文化总同盟结。

缓存是不容许实际不是缓存的,那个时候,就必要接收缓存中间件了,由缓存中间件管理缓存。

二级缓存配置

要科学的施用二级缓存,需成功如下配置的。
1 在Mybatis的构造文件中拉开二级缓存。

<setting name="cacheEnabled" value="true"/>

2 在Mybatis的映射XML中配置cache或者 cache-ref 。

<cache/>   

cache标签用于评释那几个namespace使用二级缓存,并且能够自定义配置。

<cache-ref namespace="mapper.StudentMapper"/>

cache-ref代表援引其他命名空间的Cache配置,四个命名空间的操作使用的是同一个Cache。

避坑: 为了制止那个主题材料,能够将超级缓存的等第设为 statement 级其他,那样每一次查询停止都会清掉顶级缓存。MyBatis 源码如下:

实验2

在这里次的考试中,大家扩充了对数据库的改动操作,验证在一回数据库会话中,对数据库爆发了改正操作,超级缓存是还是不是会失灵。

@Test
public void addStudent() throws Exception {
        SqlSession sqlSession = factory.openSession(true); // 自动提交事务
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        System.out.println(studentMapper.getStudentById(1));
        System.out.println("增加了" + studentMapper.addStudent(buildStudent()) + "个学生");
        System.out.println(studentMapper.getStudentById(1));
        sqlSession.close();
}

执行结果:

图片 4

我们能够看出,在改造操作后进行的一模二样查询,查询了数据库,一级缓存失效

2)statement 品级的缓存

总结

  1. Mybatis一级缓存的生命周期和SqlSession一致。
  2. Mybatis的缓存是二个粗粒度的缓存,没有更新缓存和缓存过期的概念,相同的时候只是使用了默许的hashmap,也并没有做容积上的节制。
  3. Mybatis的一流缓存最大规模是SqlSession内部,有五个SqlSession或然布满式的蒙受下,有操作数据库写的话,会孳生脏数据,提出是把一流缓存的默许等第设定为Statement,即不应用一级缓存。

图片 5

实验4

验证Mybatis的二级缓存不适于用于映射文件中留存多表查询的事态。平日的话,大家会为每贰个单表创设二个独门的映照文件,要是存在关联三个表的查询的话,由于Mybatis的二级缓存是基于namespace的,多表查询语句所在的namspace无法感应到别的namespace中的语句对多表查询中提到的表打开了改进,引发脏数据难题。

@Test
public void testCacheWithDiffererntNamespace() throws Exception {
        SqlSession sqlSession1 = factory.openSession(true); 
        SqlSession sqlSession2 = factory.openSession(true); 
        SqlSession sqlSession3 = factory.openSession(true); 

        StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
        StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
        ClassMapper classMapper = sqlSession3.getMapper(ClassMapper.class);

        System.out.println("studentMapper读取数据: " + studentMapper.getStudentByIdWithClassInfo(1));
        sqlSession1.close();
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentByIdWithClassInfo(1));

        classMapper.updateClassName("特色一班",1);
        sqlSession3.commit();
        System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentByIdWithClassInfo(1));
}

实行结果:

图片 6

在此个试验中,我们引进了两张新的表,一张class,一张classroom。class中保留了班级的id和班级名,classroom中保存了班级id和学员id。大家在StudentMapper中追加了叁个查询形式getStudentByIdWithClassInfo,用于查询学子所在的班级,涉及到多表查询。在ClassMapper中增加了updateClassName,依照班级id更新班级名的操作。
当sqlsession1的studentmapper查询数据后,二级缓存生效。保存在StudentMapper的namespace下的cache中。当sqlSession3的classMapper的updateClassName方法对class表进行翻新时,updateClassName不归属StudentMapper的namespace,所以StudentMapper下的cache未有影响到变化,未有刷新缓存。当StudentMapper中同样的查询再一次发起时,从缓存中读取了脏数据。

输出:

二级缓存介绍

在上文中涉嫌的一流缓存中,其最大的分享范围正是叁个SqlSession内部,那么怎么样让四个SqlSession之间也足以分享缓存呢,答案是二级缓存。
当张开二级缓存后,会使用CachingExecutor装饰Executor,在进入持续施行前,先在CachingExecutor实行二级缓存的询问,具体的办事流程如下所示。

图片 7

在二级缓存的选用中,叁个namespace下的有所操作语句,都影响着同二个Cache,即二级缓存是被七个SqlSession分享着的,是叁个大局的变量。
当张开缓存后,数据的查询推行的流水生产线就是 二级缓存 -> 一流缓存 -> 数据库。

日志输出能够看看,第叁次询问通过数据库查询,第二遍则未有,直接通过缓存读取。

一流缓存实验

布局完成后,通过试验的点子精通Mybatis一流缓存的机能。每两个单元测量检验后都请过来被更改的数目。
先是是成立了贰个示范表student,为其创设了相应的POJO类和增改的情势,具体能够在entity包和Mapper包中查阅。

CREATE TABLE `student` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) COLLATE utf8_bin DEFAULT NULL,
  `age` tinyint(3) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

在偏下实验中,id为1的学员名称是凯伦。