MyBatis原理
原理
- MyBatis启动时,解析mybatis的配置文件,并且从指定路径下解析mapper.xml配置文件
- 把每条sql语句映射成MappedStatement
- 然后把MappedStatement存放到Configuration的一个mappedStatements属性中(mappedStatements是一个HashMap),key为namespace + id,value为MappedStatement
- 当要执行sql语句的时候,从mappedStatements这个map中通过id找到MappedStatement
- 获取MappedStatement对应sql语句、查询参数
- 查看一级缓存中有没有数据,有则直接返回
- 缓存没有数据,则查询数据库
- 通过调用原生的jdbc方法,执行sql语句,获取到结果,删除旧缓存
- 把结果放到一级缓存,返回结果
接口方式
思考一个问题,通常的Mapper接口,我们可以不实现方法,却可以使用。这是为什么呢?答案很简单 动态代理
开始之前介绍一下MyBatis初始化时对接口的处理:
- 当判断解析到接口时,会创建此接口对应的MapperProxyFactory对象,存入HashMap中,key = 接口的class对象,value = 此接口对应的MapperProxyFactory对象。
- 当执行sql的时候,通过MapperProxyFactory生成动态代理实例(JDK动态代理)
- 通过代理实例,将请求转发给invoker方法
- 在invoke()方法中,最终SqlSession中的方法执行sql语句
MyBatis缓存
背景
一次数据库会话中,可能会执行的重复的查询语句,极短时间内重复查询,结果往往是一样的,但是造成了数据库资源的浪费。
为了解决这个问题,MyBatis使用了一级缓存。MyBatis在sqlSession对象(表示会话的对象)中建立了一个简单的缓存,将每次查询的结果缓存起来。如果下次有完全一样的查询,直接返回结果。
一级缓存
特点
-
一级缓存默认是开启的
-
作用域是session级别,每个sqlSession各自拥有一级缓存,各自隔离。
-
第一次查询结果换以key value的形式存起来,如果有相同的key进来,直接返回value,这样有助于减轻数据库的压力。
-
缓存的key格式如下:
-
cache key: id + sql + limit + offset
-
-
当commit或者rollback的时候会清除缓存,并且当执行insert、update、delete的时候也会清除缓存。
二级缓存
特点
- 一个namespace(mapper.xml)就会有一个缓存
- 不同的sqlSession之间的二级缓存是共享的
- 实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的,也就是要求实现Serializable接口
- 因为二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样
- 如果存储在内存中的话,实测不序列化也可以的。
- 一般为了避免出现脏数据,所以我们可以在每一次的insert | update | delete操作后都进行缓存刷新