# mybatis **Repository Path**: foreva/mybatis ## Basic Information - **Project Name**: mybatis - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-07-30 - **Last Updated**: 2021-01-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、简答题 ## 问题一 ### 1. **`Mybatis`动态`sql`是做什么的?** 业务场景中进行查询,需要尽量的保证`API`的简洁性。其中查询语句涉及一些逻辑分支,以及容错兜底。 但是如果在代码逻辑中直接进行该操作,会面临如下问题 - 硬编码 - 破坏代码美观,以及项目结构 - 业务变动需要重新编译发包 因此,和通过`xml`进行`sql`的配置一样,这部分逻辑都应该落在配置文件当中。 `mybatis`的动态`sql`,就是提供了这样的功能,通过指定标签的配合,能够像代码一样注入逻辑。 动态的去进行`sql`的拼接,去实现容错,或者复杂的查询功能。 > 还能够不同数据库拼接不同的`sql`哦 ### 2.都有哪些动态`sql`? | command | description | | ----------- | -------------------- | | `choose` | `switch`分支选择 | | `when` | 条件拼接 | | `otherwise` | 反向过滤 | | `if` | 基本条件判断是否拼接 | | `set` | 条件集合 | | `foreach` | 多参数判断 | | ... | ... | ### 3. 简述一下动态`sql`的执行原理? 和我们解析`sql`中的`#{id}`一样,除了这样的占位符替换和参数名称的抽取,我们还可以在`sql`解析的过程当中进行额外的操作,最简单的莫过于在最末尾添加上`limit 10`。 基于这种思想,``这种标签,后面都有对应的`handler`进行操作,判断条件是否生效。 最后,得到每个部分操作过的`sql`,校验调整操作逻辑,这都是`sql`解析过程中的操作,而不是仅限于提取参数和占位符替换。 > 实际上,同`xml`解析一样,通过特定的标签或者逻辑,去触发已经制定好的对应处理器,从而完成配置影响功能的需求。 > 如果需要,你可以自定义解析器,通过解析`xml`中配置的代码,自动加载并直接执行。 ## 问题二 ### 1. `Mybatis`是否支持延迟加载? > 在多表查询当中,一开始的关联查询是非常耗时的。 > > 使用`笛卡尔积`来描述,如果`A`表有`m`条数据,`B`表有`n`条数据,笛卡尔积就是$m \times n$. 交叉配对之后再进行的数据筛选。 > > 延迟加载,就是先完成单表的查询,然后再条件查询其他表。 > > 如上面的例子,如果`m`条中只查询出`1`条,那么,延迟加载过滤的数据集也就`n`条。 > > 当面临复数以上的表关联查询时,笛卡尔积飞速增加,性能消耗严重,查询速率缓慢。 必须支持。 ### 2. 延迟加载的实现原理是什么? 使用`CGLIB`生成代理对象,查询单表的时候进行拦截,发现条件特征,触发另一张表的条件查询。 然后将二次查询的结果,封装到首次查询的对象当中。 这样一来,每次进行的都是单表查询。 --- 不过,这样会增加查询的次数,而每次查询也会有开销。 需要依据数据量的大小,衡量关联过滤开销和查询次数的开销,选择开销最小、效率最高的方式。 ## 问题三 ### 1. `Mybatis`都有哪些`Executor`执行器? ![img](1294391-20181204205250941-1894122123.png) ### 2. 它们之间的区别是什么? | executor | character | warning | | ----------------- | ---------------------------------------------- | ------------------------------------------------- | | `BaseExecutor` | 执行器操作实现,模板方法保留逻辑插入 | 抽象类 | | `SimpleExecutor` | 调用基本方法,简单拼接`sql`后直接调用执行 | | | `ReuseExecutor` | 重用`statement`,减少重复创建开销 | 有缓存,记得刷新 | | `BatchExecutor` | 批处理模式,加快大量执行消耗 | 有缓存,记得刷新 | | `CachingExecutor` | 二级缓存执行器,维护二级缓存,减少直接查库消耗 | 直接改库,或造成数据差异 | ## 问题四 ### 1. 简述下`Mybatis`的一级、二级缓存 | level | construct | scope | invalid | | ------------- | ---------- | ------------------- | -------------------------------- | | `firstCache` | `HashMap` | `session` | `session.commit(close)` | | `secondCache` | `customer` | `mapper(namespace)` | `commit`
`service.shutdown` | > 二级缓存,默认是`Cache`,可以替换其他缓存,例如`redis`,或者分布式的缓存也可。 ## 问题五 ### 1. 简述`Mybatis`的插件运行原理 俄罗斯套娃。 基础方法生成代理对象之后,在代理操作中进行前置、后置的操作,入参和返回不变,增加额外操作。 - 代理 ```flow start=>start: origin invoker proxy=>condition: proxy ? wrapper=>operation: before() invoker.invoke() after() end=>end: proxy invoker start->proxy(no)->end proxy(yes)->wrapper->proxy ``` - 执行 最后得到的操作方法组成就是$1234567890987654321$,其中我们的具体执行方法是`0`。 这种嵌套代理,最终形成了操作方法的前置和后置的处理,提供了操作空间。 > 简单例子 > > ```java > public class Invoker implements Runnable{ > > int order; > > Invoker(int order){ > this.order = order; > } > > public void before(){ > System.err.println("before-"+order); > } > > public void after(){ > System.err.println("after-"+order); > } > > public Runnable proxy(Runnable invoke){ > return ()->{ > before(); > invoke.run(); > after(); > }; > > } > @Override > public void run() { > } > > public static void main(String[] args) { > List invokerChain = new ArrayList<>(); > for(int index = 0; index < 10; index++){ > invokerChain.add(new Invoker(index)); > } > Runnable origin = ()-> System.err.println("origin"); > Runnable proxy; > for(Invoker invoker: invokerChain){ > origin = invoker.proxy(origin); > } > origin.run(); > } > } > ``` ### 2. 如何编写一个插件? - 拦截对象 | `interceptScope` | `function` | `contains` | | ------------------ | ---------- | ------------------------------------------------------------ | | `Executor` | 执行处理 | `update`|
`query`
`commit`
`rollback`
... | | `ParameterHandler` | 参数解析 | `getParameterObject`
`setParameters`
... | | `ResultSetHandler` | 结果集处理 | `handleResultSets`
`handleOutputParameters`
... | | `StatementHandler` | `sql`构建 | `prepare`
`parameterize`
`batch`
`update`
`query`
... | - 插件类编写 ```java /** * 拦截声明 * 1. 对象 * 2. 方法 * 3. 参数 * 接口继承: Interceptor */ @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}), @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}), }) public class MyIntercept implements Interceptor { private String prefix; private String suffix; /** * 具体操作,其中一定要释放原来的操作,也就是完成方法调用 */ @Override public Object intercept(Invocation invocation) throws Throwable { String name = invocation.getMethod().getName(); System.err.println(String.format("%s - %s - %s", prefix, name, suffix)); return invocation.proceed(); } /** * 调用静态方法完成包装 */ @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } /** * 支持属性注入,通过配置修改动作 */ @Override public void setProperties(Properties properties) { prefix = properties.getProperty("prefix"); suffix = properties.getProperty("suffix"); } } ``` - 配置声明 ```xml ``` # 编程题 ## 一、配置解析 ### `mapper.xml` - `namespace` - `select` - `id` - `returnType` - `parameterType` ```java public final class MappedStatementResolver { public final static Logger logger = Logger.getLogger(MappedStatementResolver.class); public static List resolve(InputStream resource) { try{ Document read = new SAXReader().read(resource); Element rootElement = read.getRootElement(); String namespace = rootElement.attributeValue(Constants.MAPPED.NAMESPACE); List selects = rootElement.selectNodes(Constants.MAPPED.SELECT.TAG); return selects.stream().map(new Function() { @Override public MappedStatement apply(Element element) { MappedStatement mappedStatement = new MappedStatement(); mappedStatement.setId(StringUtil.combine(Constants.MAPPED.SEPARATOR, namespace, element.attributeValue(Constants.MAPPED.SELECT.ID))); mappedStatement.setReturnType(element.attributeValue(Constants.MAPPED.SELECT.RETURN_TYPE)); mappedStatement.setParameterType(element.attributeValue(Constants.MAPPED.SELECT.RETURN_TYPE)); mappedStatement.setSql(element.getTextTrim()); logger.info("load mappedStatement : " + JSON.toJSONString(mappedStatement)); return mappedStatement; } }).collect(Collectors.toList()); }catch (Exception e){ e.printStackTrace(); return null; } } } ``` ### `sqlMapConfig.xml` - `datasource` - `property` - `name` - `value` ```java public final class EnvironmentResolver { public static Environment resolver(Node node){ Node environmentNode = node.selectSingleNode(Constants.ENVIRONMENT.DATASOURCE.TAG); List properties = environmentNode.selectNodes(Constants.PROPERTY.TAG); Environment environment = new SimpleEnvironment(); for(Element property: properties){ environment.set(property.attributeValue(Constants.PROPERTY.NAME), property.attributeValue(Constants.PROPERTY.VALUE)); } return environment; } } ``` - `mapper` ```java public final class SQLConfigurationResolver { public static SQLConfiguration resolver(InputStream resource) throws DocumentException { Document configurationDocument = new SAXReader().read(resource); Environment environment = EnvironmentResolver.resolver(configurationDocument.selectSingleNode(Constants.ENVIRONMENT.DATASOURCE.TAG)); SQLConfiguration configuration = new SQLConfiguration(environment); List mappedList = configurationDocument.selectNodes(Constants.MAPPED.TAG); mappedList.stream() .map(element -> element.attributeValue(Constants.MAPPED.RESOURCE)) .map(s -> MappedStatementResolver.resolve(Resources.loadResourceAsStream(s))) .forEach(configuration::addMappedStatements); return configuration; } } ``` # 二、项目结构 没时间绘图,后面整理吧 ```mermaid graph TD A[Enviorment] B[DataSourceManager] C[Executor] D[MappedStatement] E[Session] F[Mapper] A-->B B-->C D-->C C-->E E-->F ``` ![image-20200730021737648](image-20200730021737648.png) # 三、操作区别 | function | operation | | --------------- | ---------------------- | | `executeQuery` | `query` | | `executeUpdate` | `delete`
`update` | | `execute` | `drop` |