MyBatis 基础(面试)
一、介绍MyBatis
- 优秀的半ORM 持久层框架,支持定制化SQL、存储过程以及高级映射。
- MyBatis 避免了手动设置参数以及获取结果集。
1. 什么是ORM
对象关系映射:一种解决简单Java 对象 与 关系型数据库的映射关系的技术。通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
2. 为什么是半自动的
- MyBatis 在查询关联对象或关联集合对象时,需要手动编写SQL完成,所以称之为半自动的ORM映射工具。
- 比如Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以是全自动的。
3. 优缺点
优点:
- 基于SQL 语句编程相对比较灵活,SQL 写在XML中与程序解耦,便于统一管理。
- 与JDBC 相比不需要手动开启连接,同时也减少了编写大量的代码。
- 与各种数据库兼容,能够与Spring 很好的集成。
缺点:
- SQL 编写工作量较大,关联表较多时对开发人员编写SQL 语句功底有一定的要求。
- SQL 语句依赖于数据库,导致数据库移植性较差,不能随便更换数据库。
二、MyBatis 的核心工作原理及编程步骤
1. MyBatis 的工作原理
- 读取MyBatis 的配置文件: mybatis-config.xml 为MyBatis 的全局配置文件,配置了MyBatis 的运行环境等信息。如数据库的连接
- 加载映射文件: 映射文件即SQL 映射文件,需要在MyBatis 配置文件中加载。
- 构建会话工厂: 通过MyBatis 的环境信息构建工厂SqlSessionFactory。
- 创建会话对象: 由会话工厂创建SqlSession 对象,该对象中包含了执行SQL 的所有方法。
- Executor 执行器: MyBatis 底层定义了Executor 接口来操作数据库,它将根据SqlSession 传递的参数动态生成需要执行的SQL 语句,同时负责查询缓存的维护。
- MappedStatement 对象: 在Executor 接口的执行方法中有一个MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储SQL 语句的id、参数等信息。
- 输入参数映射: 输入参数类型可以是list、map、pojo 等类型。
- 输出结果映射: 输出结果类型可以是list、map、pojo 等类型。
2. MyBatis 的编程步骤
- 创建SqlSessionFacotry;
- 通过SqlSessionFactory 创建SqlSession;
- 通过SqlSession 执行数据库操作;
- 通过session.commit() 提交事务;
- 通过session.close() 关闭会话。
3. JDBC 的工作原理
- 加载驱动,建立连接;
- 创建Statement 语句对象;
- 执行SQL 语句;
- 处理结果集;
- 关闭连接。
4. MyBatis 有哪些Executor 执行器?
有SimpleExecutor、ReuseExecutor、BatchExecutor
SimpleExecutor: 每执行一次update 或select,就开启一个Statement 对象,用完立即关闭;
ReuseExecutor: 执行update 或select,以sql 作为key 查找Statement 对象,存在就使用不存在就创建,用完后不关闭Statement 对象;
BatchExecutor: 执行update(批处理不支持select),将所有的SQL 都添加到批处理中(addBatch() ),等待统一执行(executeBatch( )),它缓存了多个Statement 对象,每个Statement 对象都是addBatch( ) 完毕后等待逐一执行executeBatch( ) 批处理。
三、为什么需要预编译?
- 定义:SQL 预编译指的是数据库驱动在发送SQL 语句和参数 给数据库管理系统之前对SQL 进行编译,这样数据库管理系统执行SQL 时就不需要重新编译。
- 为啥需要:JDBC 中使用对象PreparedStatement 来抽象预编译语句。预编译阶段可以优化SQL 的执行。多数情况下预编译的SQL 数据库管理系统可以直接运行,DBMS 不需要再次编译,越复杂的SQL,编译的复杂度越大,预编译阶段可以合并多次操作为一个操作。预编译后产生的PreparedStatement 对象缓存下来,下次对于同一个SQL 可以直接使用。MyBatis 默认情况下将所有的SQL 进行预编译。
四、MyBatis 是否支持延迟加载
- MyBatis 只支持association 关联对象和collection 关联集合对象的延迟加载。association 指的是一对一,collection 指的是一对多查询。在MyBatis 配置文件中,通过lazyLoadingEnable = true/false 来配置。
- 延迟加载的原理:使用CGLIB 创建目标对象的代理对象,当调用目标对象时进入拦截器方法。如调用 a.getB.getName(),拦截器invoke() 方法发现 a.getB() 是null 值,那么就会单独发送事先保存好的查询关联B 对象的SQL,把B 查询上来再调用setB(b) ,这样a 对象的 b 属性就有值了,接着完成 a.getB.getName() 方法调用。
五、#{} 和${} 的区别
- #{ } 是占位符,预编译处理;${ } 是拼接符,字符串替换,没有预编译处理。
- #{ } 可以有效防止SQL 注入;${ } 不能防止SQL 注入。
- MyBatis 在处理#{ } 时,#{ } 传入的参数以字符串传入,会将SQL 中的#{ } 替换为?,调用PreparedStatement 的set 方法来赋值。
六、模糊查询like 语句的写法
- “%” #{like} “%” ,需要注意因为#{ }在解析成sql 时会自动在变量外侧加单引号’ ’ ,所以%需要使用双引号不能使用单引号。
- CONCAT(’%’, #{like}, ‘%’) 使用CONCAT 函数,推荐写法!!!
- 使用bind 标签
<select id="selectUserByBind" resultType="com.po.MyUser" parameterType= "com.po.MyUser">
<!-- bind 中的 name 是 pojo 的属性名-->
<bind name="param_name" value="'%' + name + '%'"/>
select * from user where name like #{param_name}
</select>
CONCAT( ) 方法是MySQL 的函数,Oracle 的连接符号是“||”,这样不利于代码的移植。MyBatis 提供了 bind 标签解决了这一问题。
七、Mapper 如何传递多个参数
-
顺序传参法
<select id="selectUser" resultMap="UserResultMap"> select * from user where user_name = #{0} and dept_id = #{1} </select>
#{}里面的数字代表传入参数的顺序。
-
@Param(" ") 注解传参法
-
Map 传参及pojo 传参法
方法2 和方法3 都是使用#{属性名} 来接收参数
八、Dao 接口的工作原理是什么?Dao 接口里的方法参数不同时方法能重载吗?
接口的全限名就是映射文件namespace 的值,接口的方法名就是映射文件MappedStatement 的id 值,接口方法内的参数就是传递给SQL 的参数。
当调用接口方法时,接口的权限名 + 方法名拼接成字符串可以唯一定位一个MappedStatement ,MyBatis 中,<select>、<insert>、<update>、<delete> 标签都会别解析为一个MappedStatement 对象。
因为权限名 + 方法名 的保存和寻找策略,dao 接口里的方法是不能重载的。
Dao 接口的工作原理是JDK 动态代理,MyBatis 会使用JDK 动态代理为Dao 接口生成代理对象,代理对象会拦截接口方法,转而执行MappedStatement 所代表的SQL,然后将执行的结果返回。
九、不同的XML 映射文件id 是否可以相同?
如果xml 配置了namespace,则id 是可以重复的;如果没有配置namespace ,则id 是不能重复的;毕竟namespace 不是必须的。
十、MyBatis 是如何将sql 执行结果封装为目标对象并返回的?有哪些映射形式?
使用<resultMap> 标签,逐一定义列名和对象属性名之间的映射关系。
使用sql 的别名,将别名书写为对象属性名。对象属性名一般是小写,但是列名不区分大小写,MyBatis 会忽略列名大小写。把T_NAME 写成NaMe,MyBatis 照样可以正常工作。
有了列名和属性名的对象关系,MyBatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性是无法完成赋值的。
十一、MyBatis实现一对一,一对多有几种方式,怎么操作的?
有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的association,collection节点配置一对一,一对多的类就可以完成
嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过配置association,collection,但另外一个表的查询通过select节点配置。
十二、动态SQL 是什么,有哪些动态SQL 标签?
MyBatis 动态SQL 可以让我们在XML 映射文件内,以标签的形式编写动态SQL,完成逻辑判断和动态SQL 拼接功能。
MyBatis 提供了9种动态SQL 标签。
set、where、if、when、foreach、choose、otherwise、trim、bind
执行原理:使用OGNL 从SQL 参数对象中计算表达式的值,根据表达式动态的拼接SQL,以此来完成动态SQL 的功能。