Mybatis 面试题
Mybatis 动态 sql 是做什么的?都有哪些动态 sql?简述一下动态 sql 的执行原理?
Mybatis 动态 sql 是指在进行 sql 操作时,根据传入的参数对象、参数值去匹配条件进行动态判断、循环、拼接等情况。
动态 sql 大致有以下几种:
trim、where、set、foreach、if、choose、when、otherwise、bind
动态 sql 执行原理:
-
首先在解析 XML 配置文件的时候会创建 SqlSource对象,调用 LanguageDriver#createSqlSource方法
-
createSqlSource 方法底层使用 XMLScriptBuilder#parseScriptNode 方法对 XML 文件进行解析
-
parseScriptNode 方法内部会调用 parseDynamicTags 方法会把每个标签节点添加到对应的 handler 中并判断是否为动态 sql
-
然后根据返回的 DynamicSqlNode 添加到 DynamicSqlSource 中
-
根据 DynamicSqlSource#getBoundSql 方法获取到 BoundSql 对象
-
由 Executor 执行时,调用 DynamicSqlSource 解析获取 BoundSql 以及替换 sql 的参数值
Mybatis 是否支持延迟加载?如果支持,实现原理是什么?
Mybatis仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled = true|false。
原理:使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 A.getB().getName(),拦截器 invoke() 方法发现 A.getB() 是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 A.setB(b),于是 a 的对象 b 属性就有值了,接着完成A.getB().getName() 方法的调用。
Mybatis 有哪些 Executor 执行器?它们之间的区别是什么?
Mybatis 有三种基本的 Executor 执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
SimpleExecutor:每执行一次 update 或 select 操作,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。
ReuseExecutor:执行 update 或 select 操作,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map<String, Statement> 内,供下一次使用,达到重复使用Statement 对象的目的。
BatchExecutor:执行 update(没有select,JDBC批处理不支持 select 操作),将所有 sql 都添加到批处理中 addBatch() 方法,等待统一执行executeBatch() 方法,它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch() 完毕后,等待逐一执行 executeBatch() 批处理。与 JDBC 批处理相同。
作用范围:Executor 的这些特点,都严格限制在 SqlSession 生命周期范围内。
Mybatis中如何指定使用哪一种 Executor 执行器?
在 Mybatis 配置文件中可以指定默认的 Executor 执行器类型,或者给 DefaultSqlSessionFactory 创建 SqlSession 方法传递 ExecutorType 类型参数。
简述一下 Mybatis 的一级、二级缓存(分别从存储结构、范围、失效场景)
一级缓存是 SqlSession 级别的缓存,不同 sqlSession 之间的缓存结构为 HashMap,当对数据进行增、删、改或者调用 SqlSession 的 close 方法时会导致一级缓存失效。
二级缓存是 Mapper 级别的缓存,多个 SqlSession 去操作同一个 Mapper 的 sql 语句时,可以共享二级缓存,底层的缓存数据结构为 private Map<Object, Object> cache = new HashMap<>();
开启二级缓存需要在 <select>
标签上加上 useCache=“true”
,在数据更新、删除时需要手动开启 flushCache
刷新缓存。
简述 Mybatis 的插件运行原理,以及如何编写一个插件?
Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件,Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的invoke() 方法,当然,只会拦截那些你指定需要拦截的方法。实现 Mybatis 的Interceptor 接口并复写 intercept() 方法,然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可,此外还需要在配置文件中配置你编写的插件。