# 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方式】
见具体项目!