HR小姐姐带领着我来到一间洽谈室中,眼前只见一张长长的会议桌,环顾四周,我立即找了个面向窗户的位子,这样有利于面试官可以清楚地看见索大的刷脸,然后缓缓地坐下来。HR和我简单的寒暄几句后,递给我一杯水,让我稍等一会,面试官马上就来了。
索大暗暗思道:这个小姐姐人还挺好,有礼貌,声音也很温柔,面试官应该也不赖吧!
没过多久,一位身着格子衫+牛仔裤搭配,体型略显瘦高,戴着一个黑框框眼镜的男士,推开洽谈室的门。
索大礼貌性地起身问候:帅气的面试官,您好呀。
面试官仔细地翻阅索大的简历,来来回回翻了几次,心里暗道:
索大对于缓存,有些个人的见解。缓存对于系统性能来说是不可或缺的,使用缓存, 我们可以避免频繁的与数据库进行交互, 尤其是在查询越多、缓存命中率越高的情况下, 使用缓存对性能的提高更明显。
mybatis 也提供了对缓存的支持, 分为一级缓存和二级缓存。 但是在默认的情况下, 只开启一级缓存(一级缓存是对同一个 SqlSession 而言的)。
这是一张mybatis的缓存架构原理图,分别包含了 一级缓存 、 二级缓存 和 第三方缓存 的工作流程。
在mybatis的全局配置文件中添加:
<setting name="localCacheScope" value="SESSION"/> 复制代码
默认是SESSION级别 ,即在一个MyBatis会话中执行的所有语句,都会共享这一个缓存。另一种一种是 STATEMENT 级别,可以理解为缓存只对当前执行的这一个Statement有效。
一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。
一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询的结果。
每个sqlsession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。
索大通过查看源码得知,如果是insert/delete/update方法,缓存就会被刷新
mybatis一级缓存的生命周期和sqlsession一致
mybatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺
mybatis的一级缓存最大范围是sqlsession内部,有多个sqlsession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement
首先在mybatis的全局配置文件中添加:
<setting name="cacheEnabled" value="true"/> 复制代码
mybatis的映射xml文件
标签用于声明这个namespace使用二级缓存,并且可以自定义配置
代表引用别的命名空间的Cache配置,两个命名空间的操作使用的是同一个Cache
二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值
二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。 复制代码
但是,值得注意的是, 如果不提交事务,二级缓存并不起作用!
对查询频率高
,变化频率低的数据建议 使用二级缓存。
对于访问多的查询请求且用户对查询结果实时性要求不高,此时采用Mybatis二级缓存技术降低数据库 访问量,提高访问速度。
业务场景比如:
耗时较高的统计分析sql
电话账单查询 sql等。
实现方法如下:通过设置刷新时间间隔,由mybatis每隔一段时间自动清空缓存,根据数据变化 频率设置缓存的刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
mybatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,能够到namespace级别。
mybatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷。
在分布式环境下,由于默认的mybatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将mybatis的Cache接口实现,有一定的开发成本,直接使用Redis,Memcached等分布式缓存成本更低,安全性也更高
索大讲到这里,看了看面试官,好像觉得我在不知所云,于是就停止继续往下深入了!
面试官眼睛直勾勾地看着我,欲言又止地说道:
额,小伙子,要不你先讲道这,看来你是真的对mybatis缓存有了解过。
嗯嗯,索大对于mybatis插件之拦截器还是有所知晓的
是 JDK动态代理和责任链设计模式的综合运用
。采用责任链模式,通过动态代理组织多个拦截器,通过这些拦截器你可以做一些你想做的事。比如: SQL
修改,分页操作,数据过滤, SQL
执行时间性能监控等。
ParameterHandler:处理SQL的参数对象 ResultSetHandler:处理SQL的返回结果集 StatementHandler:数据库的处理对象,用于执行SQL语句 Executor:MyBatis的执行器,用于执行增删改查操作
org.apache.ibatis.plugin.Plugin
,实现 InvocationHandler 接口,插件类,一方面提供创建动态代理对象的方法,另一方面实现对指定类的指定方法的拦截处理。
如果多个插件对一个对象的方法进行拦截 就会产生层层拦截效果 第一个对象的动态代理对象传给第二个插件包装,当执行目标方法的时候先调用第二个插件的intercept 然后调用第一个插件的intercept方法 最后执行真正的目标方法。
索大当然在项目实践中使用过拦截器啊,mybatis的拦截器应用场景上文已经提及过,有 SQL
修改,分页操作,数据过滤, SQL
执行时间性能监控等。这里就如何自定义一个拦截器插件,需要注意哪些步骤来做个说明吧。
需求:我们需要对所有更新操作前打印查询语句的 sql 日志
大致分为三步骤:
实现 {@link Interceptor} 接口
添加拦截注解 {@link Intercepts}
mybatis 拦截器默认可拦截的类型只有四种,即四种接口类型 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler 对于我们的自定义拦截器必须使用 mybatis 提供的注解来指明我们要拦截的是四类中的哪一个类接口 具体规则如下: a:Intercepts 标识我的类是一个拦截器 b:Signature 则是指明我们的拦截器需要拦截哪一个接口的哪一个方法 type 对应四类接口中的某一个,比如是 Executor method 对应接口中的哪类方法,比如 Executor 的 update 方法 args 对应接口中的哪一个方法,比如 Executor 中 query 因为重载原因,方法有多个,args 就是指明参数类型,从而确定是哪一个方法
配置文件中添加拦截器
拦截器其实就是一个 plugin,在 mybatis 核心配置文件中我们需要配置我们的 plugin :
<plugin interceptor="itwukong.com.mybatis.plugin.MyInterceptor"> <property name="username" value="wukong"/> <property name="password" value="wukong"/> </plugin> 复制代码
需要值得注意的是: 拦截器顺序 1 不同拦截器顺序: Executor -> ParameterHandler -> StatementHandler -> ResultSetHandler
2 对于同一个类型的拦截器的不同对象拦截顺序: 在 mybatis 核心配置文件根据配置的位置,拦截顺序是 从上往下
面试官上下打量着我,毫不吝啬地夸了一句:
小伙子,不赖呀。
这个时候,一定不要激动,可以给个低调奢华的表情
MyBatis是一款优秀的ORM持久层框架,支持定制化SQL、存储过程以及高级映射。涉及到的知识远不止于此,我们要脚踏实地地去实践,然后理解并掌握其中的原理。
参考资料:
mybatis官网 https://mybatis.org/mybatis-3/
徐郡明 《MyBatis 技术内幕》
刘增辉 《MyBatis 从入门到精通》
以上就是本篇文章的全部内容了,谢谢大家的阅览,各位的支持和鼓励是索大前进的动力,下一篇我
们不见不散。
码字不易啊,喜欢的话不妨 点赞:+1: 关注 :heartbeat: 分享给朋友:busts_in_silhouette: ,这对我真的很重要呀!