Mybatis面试题

1、Mybatis工作原理

        

        (1) 读取Mybatis配置文件。mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息。

        (2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

        (3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。

        (4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。

        (5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。

        (6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。

        (7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。

        (8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程

2、Mybatis编程步骤

        (1)创建SQLSessionFactory对象

        (2)通过SQLSessionFactory获取SQLSession对象

        (3)通过SqlSession获得Mapper代理对象

        (4)通过Mapper代理对象,执行数据库操作

        (5)执行成功,则使用SQLSession提交事务

        (6)执行失败,则使用SQLSession回滚事务

        (7)关闭会话

3、#{}和${}的区别是什么?

        #{}是SQL的参数占位符,Mybatis会将SQL中的#{}替换为?号,在SQL执行前会使用PreparedStatement的参数设置方法。#{}是预编译处理,可以有效防止SQL注入,提高系统安全性。

        ${}是Properties文件中的变量占位符,它可以用于XML标签属性值SQL内部,属于字符串替换

4、Mybatis缓存

        Mybatis中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。

        一级缓存是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024 条 SQL。

        二级缓存是指可以跨 SqlSession 的缓存。是 mapper 级别的缓存,对于mapper 级别的缓存不同的sqlsession 是可以共享的。

5、一级缓存原理(SqlSession级别)

        

        第一次发出一个查询 sql,sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一个 map。

        同一个 sqlsession 再次发出相同的 sql,就从缓存中取出数据。如果两次中间出现 commit 操作 (修改、添加、删除),本 sqlsession 中的一级缓存区域全部清空,下次再去缓存中查询不到,所以要从数据库查询,从数据库查询到再写入缓存 。

6、二级缓存原理(Mapper级别)

        

        二级缓存的范围是 mapper 级别,mapper 以命名空间为单位创建缓存数据结构,结构是 map。mybatis 的二级缓存是通过 CacheExecutor 实现的。所有的查询操作,在 CacheExecutor 中都会先匹配缓存中是否存在,不存在则查询数据库。

7、接口绑定有几种实现方式,分别是怎么实现的?

        接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。

        接口绑定有三种实现方式:

        1)通过XML Mapper里面写SQL来绑定。在这种情况下,要指定XML映射文件里面的"namespace"必须为接口的全路径名。

        2)通过注解绑定,就是在接口的方法上面加上@Select、@Update、@Insert、@Delete注解,里面包含SQL语句来绑定。

        3)是第二种的特例,也是通过注解绑定,在接口的方法上面加上@SelectProvider、@UpdateProvider、@InsertProvider等注解,通过java代码生成对应的动态SQL。

8、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

        Mybatis仅支持association关联对象和collection关联集合对象的延迟对象。其中association指的是一对一,collection指的是一对多查询。在Mybatis配置文件中,可以配置<settingname ="lazyLoadingEnabled" value="true" />来启用延迟加载的功能。默认情况下,延迟加载的功能是关闭的。

        它的原理是:使用CGLIB或Javassist创建目标对象的代理对象。当调用代理对象的延迟加载属性的getting方法时,进入拦截器方法。比如调用a.getB().getName()方法,进入拦截器的invoke(...)方法,发现 a.getB() 需要延迟加载时,那么就会单独发送事先保存好的查询关联 B 对象的 SQL ,把 B 查询上来,然 后调用 a.setB(b) 方法,于是 a 对象 b 属性就有值了,接着完成 a.getB().getName() 方法的调用。这就 是延迟加载的基本原理。

9、Mybatis是如何进行分页的?分页插件的原理是什么?

        Mybatis使用Rowbounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非数据库分页。

        所以,实际场景下不适合直接使用Mybatis原有的RowBounds对象进行分页。而是使用如下两种方案:

        1、在SQL内直接书写带有数据库分页的参数来完成数据库分页功能

        2、也可以使用分页插件来完成数据库分页。

这两者都是基于数据库分页,差别在于前者是工程师手动编写分页条件,后者是插件自动添加分页条件。

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

        举例:SELECT * FORM student,拦截SQL后重写为:SELECT * FROM student LIMI 0,10;

10、Mybatis动态SQL是做什么的?都有哪些动态SQL?简述动态SQL的执行原理?

        Mybatis动态SQL是Mybatis的强大特性之一,可以让我们在XML映射文件内,以XML标签的形式编写动态SQL,完成逻辑判断和动态拼接SQL的功能。

        Mybatis提供了9种动态SQL标签:<if/>、<choose/>、<when /> 、<otherwise /> 、<trim/> 、 <where /> 、<set /> 、<foreach /> 、<bind />

        其执行原理为,使用 OGNL 的表达式,从 SQL 参数对象中计算表达式的值,根据表达式的值动态拼接 SQL ,以此来完成动态 SQL 的功能。

11、Mybatis的XMLMapper文件中,不同的XML映射文件,id是否可以重复?

        不同的XMLMapper文件,如果配置了"namespace",那么id可以重复;如果没有配置 "namespace",那么id不能重复。毕竟"namespace"不是必须的,只是最佳实践而已。

        原因就是,namespace + id 是作为Map<String, MappedStatement>的key使用的。如果没有"namespace",就剩下id,那么id重复会导致数据互相覆盖。如果有了"namespace",自然id就可以重复,"namespace"不同,namespace + id 自然也就不同。

12、当实体类中的属性名和表中的字段名不一样 ,怎么办?

        第一种, 通过在查询的 SQL 语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。代码如下:

<select id="selectOrder" parameterType="Integer" resultType="Order">
    SELECT order_id AS id, order_no AS orderno, order_price AS price
    FROM orders
    WHERE order_id = #{id}
</select>

        第二种,是第一种的特殊情况。大多数场景下,数据库字段名和实体类中的属性名差,主要是前者为下划线风格,后者为驼峰风格。在这种情况下,可以直接配置如下,实现自动的下划线转驼峰的功能。

<setting name="logImpl" value="LOG4J"/>
    <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

        第三种,通过 <resultMap> 来映射字段名和实体类属性名的一一对应的关系。代码如下:

<resultMap type="me.gacl.domain.Order" id=”OrderResultMap”>
    <!–- 用 id 属性来映射主键字段 -–>
    <id property="id" column="order_id">
    <!–- 用 result 属性来映射非主键字段,property 为实体类属性名,column 为数据表中的属性 -–>
    <result property="orderNo" column ="order_no" />
    <result property="price" column="order_price" />
</resultMap>

<select id="getOrder" parameterType="Integer" resultMap="OrderResultMap">
    SELECT *
    FROM orders
    WHERE order_id = #{id}
</select>

13、简述 Mybatis 的 XML 映射文件和 Mybatis 内部数据结构之间的映射关系?

       Mybatis 将所有 XML 配置信息都封装到 All-In-One 重量级对象Confifiguration内部。

        在 XML Mapper 文件中:

        1)<parameterMap> 标签,会被解析为 ParameterMap 对象,其每个子元素会被解析为 ParameterMapping 对象。
        2)<resultMap>标签,会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping 对象。
        3)每一个 <select> 、 <insert> 、 <update> 、 <delete> 标签,均会被解析为一个 MappedStatement 对象,标签内的 SQL 会被解析为一个 BoundSql 对象。

14、在 Mybatis 中你知道的动态 SQL 的标签有哪些?作用分别是什么?

        1、<if>:if是为了判断传入的值是否符合某种规则,比如是否不为空

        2、<where> where 标签可以用来做动态拼接查询条件,当和if标签配合的时候,不用显示的声明类型where 1 = 1 这种无用的条件

        3、<foreach> foreach 标签可以把传入的集合对象进行遍历,然后把每一项的内容作为参数传到 sql 语句中

        4、<include> include 可以把大量的重复代码整理起来,当使用的时候直接 include即可,减少重复代码的编写

        5、<set>适用于更新中,当匹配某个条件后,才会对该字段进行跟新操作

15、Mybatis是否可以映射Enum枚举类

        Mybatis可以映射枚举类,对应的实现类为EnumTypeHandler或EnumOrdlnalTypehandler.

        1)EnumTypeHandler,基于Enum.name属性(string)。默认

        2)EnumOrdlnalTypehandler,基于Enum.ordinal属性(int)。可通过<setting name="defaultEnumTypeHandler" value = "EnumOrdinalTypeHandler"/>来设置。

16、Mybatis都有哪些Executor执行器?他们之间的区别是什么?

        Mybatis有四种Executor执行器,分别是SimpleExecutor、ReuseExecutor、BatchExecutor、CachingExecutor。
        1)SimpleExecutor:每执行一次update或select操作,就创建一个Statement对象,用完立刻关闭Statement对象。
        2)ReuseExecutor:执行update或select操作,以及SQL作为key查找缓存的Statement对象,存在就使用,不存在就创建; 用完后,不关闭 Statement 对象,而是放置于缓存 Map<String, Statement> 内,供下一 次使用。简言之,就是重复使用 Statement 对象。
        3) BatchExecutor :执行 update 操作(没有 select 操作,因为 JDBC 批处理不支持 select 操作),将所有 SQL 都添加到批处理中(通过 addBatch 方法),等待统一执行(使用 executeBatch 方法)。它缓存了多个 Statement 对象,每个 Statement 对象都是调用 addBatch 方法完毕后,等待一次执行 executeBatch 批处 理。 实际上,整个过程与 JDBC 批处理是相同
        4)CachingExecutor :在上述的三个执行器之上,增加 二级缓存 的功能。

17、在Mapper中如何传递多个参数?

        1、使用Map集合,装载多个参数进行传递。代码如下:

// 调用方法
Map<String, Object> map = new HashMap();
map.put("start", start);
map.put("end", end);
return studentMapper.selectStudents(map);

// Mapper 接口
List<Student> selectStudents(Map<String, Object> map);

// Mapper XML 代码
<select id="selectStudents" parameterType="Map" resultType="Student">
    SELECT *
    FROM students
    LIMIT #{start}, #{end}
</select>

        2、保持传递多个参数,使用@Param注解。代码如下:

// 调用方法
return studentMapper.selectStudents(0, 10);

// Mapper 接口
List<Student> selectStudents(@Param("start") Integer start, @Param("end") Integer end);

// Mapper XML 代码
<select id="selectStudents" resultType="Student">
    SELECT *
    FROM students
    LIMIT #{start}, #{end}
</select>

        3、保持传递多个参数,不使用 @Param 注解。代码如下:

// 调用方法
return studentMapper.selectStudents(0, 10);

// Mapper 接口
List<Student> selectStudents(Integer start, Integer end);

// Mapper XML 代码
<select id="selectStudents" resultType="Student">
    SELECT *
    FROM students
    LIMIT #{param1}, #{param2}
</select>
      
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网底层民工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值