谈谈 MyBatis

源自官方文档:MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java 对象为数据库中的记录。一句话就是对 JDBC 的封装。

MyBatis 的优缺点

优点:

1:SQL 语句与 Java 代码分离,便于统一管理。

2:提供 xml 标签,支持动态 SQL 编写。

3:消除了 JDBC 中大量冗余的代码,并通过数据库连接池维护连接。

4:很好的与各种数据库兼容,能够与 Spring 很好的集成。

5:提供对象关系映射标签,支持对象与数据库的 ORM 字段关系映射。

缺点:

1:SQL 语句编写的工作量较大,尤其是字段多,关联查询复杂时对于 SQL 的基础功底有一定要求。

2:对性能要求较高,需求变化较多的项目适合使用 MyBatis。

什么是 ORM

对象关系映射(Object Relational Mapping,简称 ORM),描述实体类属性和数据库字段之间的映射关系,将面向对象语言程序中的对象自动持久化到关系型数据库中,场景的 ORM 框架:Hibernate、MyBatis 等。

半自动和全自动 ORM 映射工具

Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或关联集合对象时,可以根据对象关系模型直接获取。

MyBatis 属于半自动 ORM 映射工具,使用 MyBatis 查询关联对象或关联集合对象时,需要手动编写 SQL 来完成。

JDBC 的不足和 MyBatis 的解决

1:JDBC 数据库创建链接和释放资源频繁造成系统资源浪费从而影响系统性能。MyBatis 使用数据库连接池管理数据库连接。

2:JDBC 的 SQL 语句写在代码中造成代码不易维护。MyBatis 将 SQL 语句配置在 mapper.xml 文件中与 Java 代码进行分离。

3:JDBC 的 SQL 语句传参非常麻烦,where 条件不确定且占位符要与参数一意对应。MyBatis 自动将 Java 对象映射到 SQL 语句中。

4:JDBC 对结果集解析较为麻烦,SQL 语句变化导致解析代码变化且解析前需要遍历。MyBatis 自动将 Java 对象映射到 SQL 语句中。

MyBatis 的编码步骤

1:创建 SqlSessionFactory。

2:通过 SqlSessionFactory 创建 SqlSession。

3:通过 SqlSession 执行数据库操作。

4:调用 session.commit() 提交事务。

5:调用 session.close() 关闭会话。

MyBatis 中 #{} 和 ${} 的区别

​${}​​ 是 properties 文件中的变量占位符。用于标签属性值和 SQL 内部的静态文本替换。

​#{}​​​ 是 SQL 语句参数的占位符。MyBatis 会将 SQL 语句中的 ​​#{}​​ 替换为 ? 号。

​#{}​​​ 方式能很大程度上防止 SQL 注入,一般使用 ​​#{}​​​ 而不使用 ​​${}​​​。​​${}​​ 无法防止SQL 注入,一般用于传入数据库对象,比如表名。

什么是 SQL 注入

源自百度百科:SQL 注入即是指 web 应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在 web 应用程序中事先定义好的查询语句的结尾上添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。一句话就是通过拼接非法的可执行的 SQL 语句非法获取数据信息。

MyBatis 的 mapper 接口调用的要求

1:mapper 接口的类路径即是 mapper.xml 文件中的 namespace 属性值。

2:mapper 接口方法名和 mapper.xml 文件中定义的每个 SQL 的 id 相同。

3:mapper 接口方法的入参类型和 mapper.xml 文件中定义的每个 SQL 的 parameterType 的类型相同。

4:mapper 接口方法的返回值类型和 mapper.xml 文件中定义的每个 SQL 的 resultType 的类型相同。

MyBatis 中的一级缓存与二级缓存

一级缓存:基于 PerpetuaCache 的 HashMap 本地缓存,作用域为 Session。当 Session flush 或 close 后,该 Session 中的所有 Cache 就将被清空。

二级缓存:与一级缓存机制相同,也是基于 PerpetuaCache 的 HashMap 本地缓存,但其作用域为 namespaces。二级缓存可以自定义存储源,例如 Ehcache。二级缓存会对一个 namespace 对应的 mapper.xml 文件中所有的 select 操作结果都进行缓存,不同线程之间就可以共用二级缓存。

当某一个作用域,不管是一级缓存 Session 还是二级缓存 namespaces 进行了增删改操作,默认该作用域下所有 select 中的缓存将被清除。

二级缓存开启:MyBatis 配置文件中开启 <cache />。

二级缓存的策略:<cache readOnly="true"/> 二级缓存返回给所有调用者是同一个缓存对象实例,调用者更新缓存实例后可以会造成其他调用者调用时出现数据不一致的情况。<cache readOnly="false"/> 二级缓存返回给所有调用者是总缓存对象的拷贝,不同调用者获取的是缓存对象的不同实例,各自维护各自的缓存对象,更新不会影响到其他的调用者。二级缓存默认使用安全的 <cache readOnly="false"/> 策略。

xml 文件中,除了常见的 CRUD 标签外还有哪些标签

其他 5 个标签 resultMap / parameterMap / sql / include / selectKey。

动态 SQL 的 9 个标签:trim / where / set / foreach / if / choose / when / otherwise / bind。

mapper 接口的实现原理

mapper 接口的底层是通过 Map<String, MapperedStatement> 集合维护。key 为接口的路径名 + 方法名拼接的字符串或 namespace + id,value 为 MapperedStatement 对象。MapperedStatement 对象即为 namespace 下的标签 id 对应的对象,例如 namespace=”com.xpy.mapper.UserMapper” 下的 select / insert / update / delete 标签 id=”getUserById” 对应的对象。接口的路径名 + 方法名拼接的字符串,例如 com.xpy.mapper.UserMapper.getUserById 定位一个唯一的 MapperedStatement 对象。

MapperedStatement 对象唯一说明 mapper 接口中的的方法不能重载。

不同的 xml 文件中,如果配置了 namespace,id 就可以重复,如果没有配置 namespace,则 id 对应的数据就会造成覆盖。

mapper 接口的工作原理是 JDK 动态代理。MyBatis 运行时会使用 JDK 动态代理为 mapper 接口生成代理对象 proxy,代理对象 proxy 会拦截接口方法转而去执行 MapperedStatement 对象,并将 SQL 的执行结果返回。

MyBatis 分页和分页插件的原理

MyBatis 使用 RowBounds 对象进行分类,是针对 ResultSet 结果集执行的内存分页。要实现物理分页可以通过 SQL 中配置 LIMIT 参数或使用分页插件。

分页插件的原理是使用 MyBatis 提供的插件接口实现自定义插件,在插件的拦截方法内拦截待执行的 SQL 语句进行重写,根据 dialect 方言添加对应的物理分页语句和参数。

MyBatis 的插件运行原理

MyBatis 仅支持 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这四种接口的插件。每当执行这四种接口对象的方法时,就会进入拦截方法,也就是 InvocationHandler 的 invoke 方法,拦截指定需要拦截的方法。

编写一个插件只需实现 MyBatis 的 Interceptor 接口并重写 intercept 方法,然后在给插件编写一个注解,指定要拦截哪一个接口的哪些方法,最后在配置文件中配置编写的插件。

MyBatis 如何将 SQL 执行结果封装为目标对象返回的

第一种是通过 resultMap 标签逐一定义数据库字段名和实体类属性名之间的映射关系,第二种是使用别名,将数据库字段名写成实体类属性名,例如 t_title as title。有了映射关系后,MyBatis 会通过反射创建对象,同时给对象的属性逐一赋值并返回,找不到或没有映射关系的属性无法完成赋值。

MyBatis 的动态 SQL 的执行原理

动态 SQL 是指可以在 xml 文件中可以通过标签的形式动态编写 SQL,完成逻辑判断和动态拼接 SQL 语句的功能。动态 SQL 的执行原理是使用 OGNL 从 SQL 参数对象中计算表达式的指,根据表达式的值动态拼接 SQL。

MyBatis 关联查询的实现方式及区别

MyBatis 支持一对一、一对多、多对一、多对多的关联查询。关联对象查询有两种实现方式,第一种是单独发送一个 SQL 语句去查询关联对象,赋值给主对象并返回主对象。第二种是使用嵌套查询,使用 join 查询,一部分列是 A 对象的属性值,一部分列是 B 对象的属性值,只发送一个 SQL 语句就可以把主对象和其关联对象全都查出来。

MyBatis 的 Executor 执行器

MyBatis 有三种基本的 Executor 执行器:SimpleExecutor、ReuseExecutor、BatchExecutor。这些执行器都严格限制在 SqlSession 生命周期范围内。

SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭。

ReuseExecutor:执行 update 或 select 时,以 SQL 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后不关闭,而是放到 Map<String, Statement> 中,供下一次使用。

BatchExecutor:在执行 update 时将所有 SQL 都添加到批处理中,等待统一执行,执行器缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch 完毕后,等待逐一执行 executeBatch。JDBC 批处理不支持 select。

MyBatis 配置文件中可以指定默认的 ExecutorType 执行器类型,也可以手动给 DefaultSqlSessionFactory创建 SqlSession 的方法传递 ExecutorType 参数类型。

MyBatis 是否可以映射 Enum 枚举类

可以,MyBatis 可以映射任何对象到表的列上。映射方式为自定义一个 TypeHandler,实现 TypeHandler 的 setParameter 和 getResult 方法,setParameter 方法实现 javaType 到 jdbcType 的转换,设置 SQL 占位符参数。getResult 方法实现 jdbcType 到 javaType 的转换,获取列查询结果。

MyBatis 解析标签规则

MyBatis 解析 xml 文件是按照顺序解析的,但 MyBatis 解析标签时如果发现引用了另外一个标签,但另外一个标签尚未解析到,此时就会将此标签标记为未解析状态,继续解析余下的标签,待所有标签解析完毕,MyBatis 会重新解析被标记为未解析状态的标签,此时另一个标签已经存在,也就正常解析完成了。所以如果一个标签通过 include 引用了另外一个标签的内容,则另外一个标签可以定义在任何地方。

 

 


作者:​​靠谱杨​​​,

更多日常分享尽在我的VX公众号:小杨的挨踢IT生活

谈谈MyBatis持久层框架_xml