Mybatis
零散的一些知识
- mybatis中生成对应的映射接口对象使用了java原生的动态代理实现的
- mybatis执行sql的过程
缓存
- 一级缓存
- 一级缓存位于SqlSession中,每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成
MappedStatement
,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache
,最后返回结果给用户。 - 对一级缓存的操作其实就是对一个hashMap进行操作,key是由
MappedStatement
的Id、SQL的offset、SQL的limit、SQL本身以及SQL中的参数构成 - 配置:开发者只需在MyBatis的配置文件中,添加如下语句,就可以使用一级缓存。共有两个选项,
SESSION
或者STATEMENT
,默认是SESSION
级别,即在一个MyBatis会话中执行的所有语句,都会共享这一个缓存。一种是STATEMENT
级别,可以理解为缓存只对当前执行的这一个Statement
有效。有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。1
<setting name="localCacheScope" value="SESSION"/>
- 一级缓存位于SqlSession中,每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成
- 二级缓存
如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。
二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。
MyBatis的二级缓存不适应用于映射文件中存在多表查询的情况。通常我们会为每个单表创建单独的映射文件,由于MyBatis的二级缓存是基于
namespace
的,多表查询语句所在的namspace
无法感应到其他namespace
中的语句对多表查询中涉及的表进行的修改,引发脏数据问题。为了解决脏数据的问题,可以使用Cache ref,让ClassMapper
引用StudenMapper
命名空间,但这样做使缓存的粒度变粗了,多个Mapper namespace
下的所有操作都会对缓存使用造成影响。配置:
- <****setting**** name=”cacheEnabled” value=”true”/>
- 在MyBatis的映射XML中配置cache或者 cache-ref 。cache标签用于声明这个namespace使用二级缓存,并且可以自定义配置。
type
:cache使用的类型,默认是PerpetualCache
,这在一级缓存中提到过。eviction
: 定义回收的策略,常见的有FIFO,LRU。flushInterval
: 配置一定时间自动刷新缓存,单位是毫秒。size
: 最多缓存对象的个数。readOnly
: 是否只读,若配置可读写,则需要对应的实体类能够序列化。blocking
: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
cache-ref
代表引用别的命名空间的Cache配置,两个命名空间的操作使用的是同一个Cache。
在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。