# mybatis-learn **Repository Path**: self_built_learning/mybatis-learn ## Basic Information - **Project Name**: mybatis-learn - **Description**: 学习mybatis仓 - **Primary Language**: Java - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2020-03-26 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 一、简单题 1、Mybatis动态sql是做什么的?都有哪些动态sql?简述一下动态sql的执行原理? ​ 动态的sql是可以通过所需条件,集合数据,生成SQL. ​ 业务查询或修改的时候,可以根据字段非空来对数据查询或修改部分数据的。 set、if 标签: update 时候可以判断非空设置字段值 ```xml name=#{name}, ``` where、if 标签:where时候可以判断非空来设置条件。 ```xml and name = #{name} ``` foreach 标签: 传入是一个集合的时候,可以通过这个来生成一个按照规定分隔符和前后符号的sql部分,例如 userId in (1,2) . ```xml #{item} ``` choose、when、otherwise 标签: 这三个标签一起使用,when选择条件成立执行跳出,没有成立则otherwise,只会执行一次 ```xml createTime > #{createTime} createTime < #{createTime} createTime = #{createTime} ``` 动态sql的执行原理: ``` 创建步骤: XMLMapperBuilder 解析Mapper.xml XMLStatementBuilder 解析 select|insert|update|delete LanguageDriver进行解析SQL 默认情况 XMLLanguageDriver 实现 XMLLanguageDriver 创建 SqlSource XMLScriptBuilder 解析context内的sql 得到 MixedSqlNode实例。 MixedSqlNode实例 包含着sql的内容层,StaticTextSqlNode,WhereSqlNode,IfSqlNode 在执行解析的时候文本直接作为StaticTextSqlNode, 则进入WhereHandler 再次调用 parseDynamicTags解析 则进入IfHandler 再次调用parseDynamicTags解析 成一个MixedSqlNode 包含多层SqlNode实例, 最终创建 DynamicSqlSource(SqlSource)。 sqlSource 设置在MappedStatement中。完毕! ``` ``` 执行步骤: BoundSql boundSql = mappedStatement.getBoundSql(parameter); 实际上 通过 sqlSource 生成 BoundSql的过程,传入一个参数,通过参数来完成后续 sql 解析 DynamicSqlSource.getBoundSql() 解析sql this.rootSqlNode.apply(context); 每个节点的子节点一层层调用 apply 方法,解析合并SQL SqlSourceBuilder 替换#{} 为 ? 并得到 StaticSqlSource sqlSource; BoundSql boundSql = sqlSource.getBoundSql(parameterObject); 最终 new BoundSql(configuration, sql, parameterMappings, parameterObject); 得到 BoundSql ``` 2、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么? 支持延迟加载。通过 CGLIB 进行类动态代理实现,(CGLIB生成增强版的子类) 例如源码中:EnhancedResultObjectProxyImpl implements MethodInterceptor 当调用被代理方法时,会执行 intercept 具体实现进行增强的过程。 3、Mybatis都有哪些Executor执行器?它们之间的区别是什么? ``` BaseExecutor ReuseExecutor 同一请求进行了Statement复用 BatchExecutor 批量删除和批量插入 SimpleExecutor 每次创建一个Statement进行执行sql CachingExecutor 在 BaseExecutor 实现上包装了一层二级缓存,cacheEnabled=true时并且配置cache则开去全局的缓存。 ``` 4、简述下Mybatis的一级、二级缓存(分别从存储结构、范围、失效场景。三个方面来作答)? 一级缓存实际上是一个HashMap 进行的本地缓存,在query的时候,会判断缓存是否存在,不存在则从数据库读取并更新缓存。当发生update和commit的时候会被清除。每个SqlSession有效,存在于(BaseExecutor -> PerpetualCache) 二级缓存是实现的全局缓存,cacheEnabled 启动开启二级缓存,被 CachingExecutor 进行包装。如果是使用本地缓存的实现则单应用环境的SqlSession 有效,如果Redis,memcache等则 服务器配置的 Sqlsession 获取Cache命名一致的均有效。当update操作会标记 clearOnCommit,commit的时候会调用二级缓存实现的clear。 当Update 之后,发生当select 会将数据记录到 entriesToAddOnCommit (发生update之前的则会清除)。 在commit 清空所有缓存之后,会从entriesToAddOnCommit进行还原后面的缓存。 5、简述Mybatis的插件运行原理,以及如何编写一个插件? Mybatis插件,实际上是一个Java 接口的动态代理实现。通过配置插件,层层代理指定需要被增强的接口中的方法。 ``` ParameterHandler 参数设置处理 ResultSetHandler 结果封装处理 StatementHandler SQL语法构建 interceptorChain.pluginAll(statementHandler); 获得最终的代理对象 ``` ```java // 实现一个插件 @Intercepts( @Signature(type = StatementHandler.class ,method="query" , args= {Statement.class, ResultHandler.class}) ) public class MyPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { try { System.out.println("接着我就开始执行了"); return invocation.proceed(); } finally { System.out.println("然后我就执行结束了"); } } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } public void setProperties(Properties properties) { } } ``` ```xml ``` 二、编程题 请完善自定义持久层框架IPersistence,在现有代码基础上添加、修改及删除功能。【需要采用getMapper方式】 见具体项目!