飞道的博客

MyBatis常见面试题

383人阅读  评论(0)

目录

一、使用方面的问题

1、什么是MyBatis?

2、为什么说MyBatis是半ORM框架?与Hibernate有哪些不同?

3、MyBatis的优缺点:略

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

5、MyBatis是怎么解决实体类中的属性名和表中的字段名不一样的问题?

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

7、MyBatis的接口绑定是什么?有哪些绑定方式?

8、在MyBatis中使用Mapper接口开发时有哪些要求?

二、源码方面的问题

1、MyBatis面向Mapper接口编程的工作原理是什么?

2、MyBatis中Mapper接口中的方法支持重载么?

3、MyBatis中动态SQL的执行原理是什么?

4、MyBatis的工作原理是什么?

4.1 工作原理图解

4.2 MyBatis的执行流程大概可以分为八个步骤,分别是:

5、MyBatis的缓存是什么?

6、MyBatis中一对一查询、一对多查询是怎么实现的

6.1 一对一关联查询举例如下:

6.2 一对多关联查询举例如下:

7、MyBatis的分页原理是什么

8、MyBatis的动态SQL是什么?主要标签有哪些?

9、MyBatis用到了哪些设计模式?

10、MyBatis中都有哪些Executor执行器?它们之间的区别是什么?

11、MyBatis的Xml映射文件和MyBatis内部数据结构之间的映射关系?

99、参考


一、使用方面的问题

1、什么是MyBatis?

  1. MyBatis是一个半ORM框架,它内部封装了JDBC,开发的时候只需要关注SQL语句本身就可以了,我们不需要花太多精力去处理原生的JDBC那一套流程,比如 加载驱动、创建connection连接、创建statement、创建SqlSesssionFactory、创建SQLSession等一系列比较繁杂且重复性比较高的过程。
  2. MyBatis可以使用 SQL映射文件XML的方式 或者 注解的方式 来配置映射信息,将Java对象映射成数据库当中的记录,或者将数据库当中的记录反映射成Java对象。
  3. MyBatis也支持动态SQL,它是通过Java对象和statement对象当中SQL的动态参数进行映射从而生成最终要执行的SQL语句,最后由MyBatis框架执行SQL并将结果映射为Java对象返回的。

2、为什么说MyBatis是半ORM框架?与Hibernate有哪些不同?

  1. ORM,它的意思是指对象和关系之间的映射,包括【对象->关系的映射】和【关系->对象的映射】两个方面。
  2. Hibernate是个完整的ORM框架,它实现了这两个方面的功能。而MyBatis只完成了【关系->对象的映射】,准确地说,MyBatis是SQL映射框架而不是ORM框架,因为它仅有字段映射,对象数据以及对象的实际关系仍然需要通过手写SQL来实现。
  3. MyBatis可以直接编写原生SQL,可严格控制SQL的执行性能,灵活度比较高。Hibernate只能通过编写HQL来实现数据库查询。

3、MyBatis的优缺点:略

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

  1. #{ } 是预编译处理当中的一个占位符。MyBatis在处理 #{ } 的时候,会将SQL中的 #{ } 替换成 ,然后,再调用PreparedStatement对象的set方法进行赋值,由此来防止SQL注入的问题。使用预编译的优势在于,一次编译,多次运行,省去了边执行边解析优化的过程。
  2. ${ } 是单纯的字符串文本替换。MyBatis在处理 ${ } 的时候,只是简单的把 ${ } 替换为变量的值而已,由此会造成SQL注入,带来不必要的风险。
  3. 大部分情况下,我们都是使用 #{ } 来处理业务。但是,针对一些特殊的情况,比如 通过一个“变化的字段”做排序等,也可以使用 ${ }

#{parm} 传入的参数会被当成一个字符串,会对自动传入的数据加一个双引号,举例:select * from table where name = #{param},则解析成的sql为:select * from table where name =   "id"

${param} 传入的参数会被当成SQL语句中的一部分,举例:order by ${param},则解析成的sql为:order by id

5、MyBatis是怎么解决实体类中的属性名和表中的字段名不一样的问题?

在MyBatis中,关于实体类和表中字段名不一致的问题,主要有两种解决方式:

(1)第一种是使用 <resultMap> 标签,逐一定义列名和实体类对象属性名之间的映射关系。


  
  1. <resultMap id="orderResultMap" type="com.wind.OrderEntity">
  2. <!–用id标签来映射主键字段,用result属性来映射非主键字段–>
  3. <!–其中,用property为实体类属性名,column为数据表中的属性–>
  4. <id property="id" column="user_id">
  5. <result property="no" column="user_no"/>
  6. </reslutMap>

(2)第二种是使用在SQL中定义列的别名,将列的别名与实体类对象的属性名一一对应起来,比如 T_Name as name,实体类对象的属性名一般是小写,但是列名不区分大小写,MyBatis也会忽略列名的大小写。


  
  1. <select id="selectUserById" parameterType="java.lang.Integer" resultetype="com.wind.UserEntity">
  2. select user_id as id, user_no as no from Test where user_id = #{id}
  3. </select>

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

(1)第一种是使用 @param 注解的方式。比如:

user selectUser(@param("username") string username, @param("password") string password);

(2)第二种是使用Java对象的方式。此时,在Java对象中可以有多个属性,每一个属性其实都是一个参数,这样也可以实现在Mapper中传递多个参数。

(3)第三种是使用map集合的方式。此时,需要使Mapper接口方法的输入参数类型和mapper.xml中定义的每个SQL的parameterType的类型都相同。

7、MyBatis的接口绑定是什么?有哪些绑定方式?

  1. 接口绑定,就是在MyBatis中定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口中的方法就可以操作数据库了。这样比起直接使用SqlSession对象提供的原生态的方法,更加灵活与简单。
  2. 接口绑定主要有两种实现方式:(1)第一种是通过注解绑定,也就是说 在接口的方法上面加上 @Select、@Update 等注解,注解里面包含SQL语句来进行绑定。这种方式可以省去SQL的 xml 映射文件,对于简单的SQL来说比较适用,后期维护比较困难,平时在业务中基本不怎么使用。(2)第二种是通过在SQL的xml 映射文件里面写SQL来进行绑定, 在这种情况下,要指定 xml 映射文件里面的 namespace 参数必须为接口的全类名。不管是SQL简单还是复杂,xml 文件的方式 都比较简单高效,也是最常用的。

8、在MyBatis中使用Mapper接口开发时有哪些要求?

  1. 一般情况下,在日常开发的时候,会遵循一个mapper.xml映射文件对应于一张表的增删改查。
  2. 其中,mapper.xml映射文件中的namespace属性,必须要定义为对应的Mapper接口的全类名,以此来标识一个mapper级别的二级缓存。
  3. Mapper接口中的方法名要和mapper.xml中定义的每个SQL语句的id属性相同。
  4. Mapper接口中的方法的输入参数类型要和mapper.xml中定义的每个SQL语句的parameterType的类型相同。
  5. Mapper接口中的方法的输出参数类型要和mapper.xml中定义的每个SQL语句的resultType的类型相同,或者使用resultMap也行。

二、源码方面的问题

1、MyBatis面向Mapper接口编程的工作原理是什么?

  1. MyBatis是面向Mapper接口编程的。一般情况下,一张表对应于一个SQL映射文件mapper.xml,一个SQL映射文件又对应于一个Mapper接口。
  2. Mapper接口,它是没有实现类的。当调用接口方法的时候,它是采用了JDK的动态代理的方式,先从Configuration配置类MapperRegistry对象中获取Mapper接口和对应的代理对象工厂MapperProxyFactory,然后利用代理对象工厂MapperProxyFactory创建实际代理类MapperProxy,最后在实际代理类中通过MapperMethod类对象内保存的对应方法的信息,以及对应的SQL语句的信息进行分析,最终确定对应的增强方法进行调用。

2、MyBatis中Mapper接口中的方法支持重载么?

  1. 在MyBatis中,Mapper接口中的方法不支持重载。
  2. 在MyBatis源码中有这么几行代码,我们可以看到,在解析XML配置文件创建Mapper接口对应方法的时候,采用了【 接口全类名+方法名】的方式作为 StrictMap(MappedStatement数据存放的Map集合)的key值,而源码中对于StrictMap的put方法进行了判断,如果存入的数据key已重复,则会抛出异常,所以,Mapper接口中的方法不支持重载。

3、MyBatis中动态SQL的执行原理是什么?

在MyBatis中,动态SQL的执行原理大致可以分为三个阶段:初始化阶段、代理阶段、数据读写阶段,每个阶段都在做不同的事情。

  1. 初始化阶段:通过 XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder 来解析XML配置文件中的信息 存储到Configuration类中的。
  2. 代理阶段:首先从Configuration配置类MapperRegistry对象中获取Mapper接口和对应的代理对象工厂信息,然后再利用代理对象工厂MapperProxyFactory创建实际代理类,最后在MapperProxy类中通过MapperMethod类对象内保存的对应方法的信息,以及对应的SQL语句的信息进行分析,最终确定对应的增强方法的调用。
  3. 数据读写阶段:通过四种Executor调用四种Handler进行数据查询和数据封装返回。

4、MyBatis的工作原理是什么?

4.1 工作原理图解

4.2 MyBatis的执行流程大概可以分为八个步骤,分别是:

  1. 首先会读取MyBatis的配置文件【mybatis-config.xml】,配置文件中配置了MyBatis的运行时环境信息,例如数据库连接信息等。
  2. 然后会加载MyBatis的映射文件【xxxMapper.xml】,映射文件也就是SQL映射文件,SQL映射文件中配置了操作数据库的SQL语句,映射文件需要在【mybatis-config.xml】中加载。【mybatis-config.xml】中可以加载多个不同的映射文件,每个映射文件都对应着数据库中的一张表。
  3. 接着会通过MyBatis的配置文件构造出一个全局的会话工厂对象【SqlSessionFactory】。
  4. 再通过会话工厂对象来创建会话对象【SqlSession】,会话对象中包含了执行SQL语句的所有方法。
  5. 在执行SQL语句时,其底层使用的是一个执行器Executor接口来操作数据库,它将根据会话对象【SqlSession】传递过来的参数动态的生成需要执行的SQL语句,同时会负责查询缓存的维护。
  6. 在执行器【Executor】接口的执行方法中有一个MappedStatement对象,这个对象是对映射关系的封装,包括了输入参数映射和输出结果映射。
  7. 在输入参数映射中,输入参数可以是普通的基本数据类型,也可以是POJO类型、或者是Map/List等集合类型。输入参数映射的过程,类似于原生JDBC对preparedStatement对象设置参数的过程。
  8. 在输出结果映射中,输出结果可以是基本数据类型,也可以是POJO类型、或者是Map/List等集合类型。输出结果映射的过程,类似于原生JDBC对结果集做解析的过程。

5、MyBatis的缓存是什么?

  1. 在MyBatis中,有一级缓存和二级缓存之分。默认情况下,它的一级缓存是打开的,而且不能关闭;二级缓存是关闭的,如果想要使用的话,需要手动开启,做一些配置就行。
  2. 一级缓存是SqlSession级别的缓存,一个sqlSession对象代表着我们的程序和数据库的一次会话。当MyBatis第一次发出一个SQL查询的时候,SQL的查询结果首先会写入SqlSession的一级缓存中,缓存使用的数据结构是一个HashMap,HashMap的key=MapperID+offset+limit+Sql+所有的入参,HashMap的value=查询结果;然后再把查询结果返回给用户。当同一个SqlSession再次发出同样的SQL查询的时候,就会直接从一级缓存中取出数据,这在一定程度上提高了查询效率。但是,需要注意的是,如果在两次查询中出现了DML SQL(insert、delete、update),则本SqlSession的一级缓存就会被清空,下次再去查询的时候,就会直接从数据库中查询。另外,一级缓存中最多可以缓存1024个SQL的key引用查询结果。
  3. 二级缓存是Mapper级别的缓存,可以跨SqlSession对象,不同的SqlSession对象是可以共享的。因为MyBatis是面向Mapper接口编程的,一般情况下,一张表对应于一个Mapper映射文件,一个Mapper映射文件又对应于一个Mapper接口,在一个Mapper映射文件中我们通常会设置一个namespace属性,这个namespace属性就标识了这个Mapper接口,所以,MyBatis是以命名空间为单位创建二级缓存的。二级缓存,它也是一个HashMap的数据结构,key=MapperID+offset+limit+Sql+所有的入参,value=查询结果。如果开启了二级缓存,那么缓存的查询顺序是怎样的呢?是这样的,所有的SQL查询都会首先看一下是否命中了二级缓存,如果命中了则直接从二级缓存中取出结果并返回;如果没命中,再去看一下是否命中了一级缓存,如果命中了则直接从一级缓存中取出结果,如果还是没命中,则再去查询数据库。对于查询结果,也是先写入一级缓存、再写入二级缓存、最后再返回给用户的这样一个流程。
  4. 同时,MyBatis主要是做ORM的,对于缓存它并没有花费十足的精力,所以,它还提供了一些缓存接口以供第三方使用,用来提高缓存性能,比如常用的是EnCache缓存框架,它是一个纯Java的进程内缓存框架,性能很高。
  5. 对于MyBatis的缓存更新机制,当某一个作用域(一级缓存 sqlSession;二级缓存namespace)的进行了写操作后,也就是 insert、delete、update 等DML操作,默认情况下,该作用域下的所有select查询缓存都将被清除,并重新更新;如果开启了二级缓存,则只根据配置判断是否刷新。

6、MyBatis中一对一查询、一对多查询是怎么实现的

  1. 在MyBatis中,使用association标签来解决一对一的关联查询。association标签可用的属性有:(1)property:对象属性的名称(2)javaType:对象属性的类型(3) column:对应的外键字段名称(4)select:使用另一个查询封装的结果。
  2. 在MyBatis中,使用collection标签来解决一对多的关联查询。collection标签可用的属性有:(1)property:指的是集合属性的值(2)ofType:指的是集合中元素的类型(3)column:所对应的外键字段名称(4)select:使用另一个查询封装的结果。

6.1 一对一关联查询举例如下:


  
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.wind.repository.StudentRepository">
  4. <resultMap id="studentMap" type="com.wind.entity.StudentEntity">
  5. <result column="Id" property="id"/>
  6. <result column="Name" property="name"/>
  7. <result column="ClassId" property="classId"/>
  8. <result column="Status" property="status"/>
  9. <result column="AddTime" property="addTime"/>
  10. <result column="UpdateTime" property="updateTime"/>
  11. </resultMap>
  12. <resultMap id="studentMap2" type="com.wind.entity.StudentEntity">
  13. <result column="Id" property="id"/>
  14. <result column="Name" property="name"/>
  15. <result column="ClassId" property="classId"/>
  16. <result column="Status" property="status"/>
  17. <result column="AddTime" property="addTime"/>
  18. <result column="UpdateTime" property="updateTime"/>
  19. <association property="classEntity" javaType="classEntity">
  20. <id column="cId" property="id"/>
  21. <result column="cClassName" property="className"/>
  22. <result column="cStatus" property="status"/>
  23. </association>
  24. </resultMap>
  25. <sql id="sql_select">
  26. select Id, Name, ClassId, Status, AddTime, UpdateTime from RUN_Student
  27. </sql>
  28. <select id="queryStudent" parameterType="int" resultMap="studentMap">
  29. <include refid="sql_select"/>
  30. where id = #{id} and status = 1
  31. </select>
  32. <select id="queryStudentWithClass" parameterType="int" resultMap="studentMap2">
  33. select r.Id, r.Name, r.ClassId, r.Status, r.AddTime, r.UpdateTime, c.id as cid, c.ClassName as cClassName, c.Status as cStatus
  34. from RUN_Student r join RUN_Class c on r.classId = c.id
  35. where r.id = #{id}
  36. </select>
  37. </mapper>

6.2 一对多关联查询举例如下:


  
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.wind.repository.ClassRepository">
  4. <resultMap id="classStudentMap" type="com.wind.entity.ClassEntity">
  5. <result column="Id" property="id"/>
  6. <result column="ClassName" property="className"/>
  7. <result column="Status" property="status"/>
  8. <result column="AddTime" property="addTime"/>
  9. <result column="UpdateTime" property="updateTime"/>
  10. <collection property="studentEntities" ofType="com.wind.entity.StudentEntity">
  11. <result column="sId" property="id"/>
  12. <result column="sName" property="name"/>
  13. <result column="sClassId" property="classId"/>
  14. <result column="sStatus" property="status"/>
  15. <result column="sAddTime" property="addTime"/>
  16. <result column="sUpdateTime" property="updateTime"/>
  17. </collection>
  18. </resultMap>
  19. <sql id="sql_select_join_student">
  20. select c.Id, c.ClassName, c.Status, c.AddTime, c.UpdateTime,
  21. s.Id as sId, s.Name as sName, s.ClassId as sClassId, s.Status as sStatus, s.AddTime as sAddTime, s.UpdateTime as sUpdateTime
  22. from RUN_Class c join RUN_Student s on c.Id = s.classId
  23. </sql>
  24. <select id="queryClassByClassId" parameterType="int" resultMap="classStudentMap">
  25. <include refid="sql_select_join_student"/>
  26. where c.id = #{id} and c.status = 1 and s.status =1
  27. </select>
  28. </mapper>

7、MyBatis的分页原理是什么

(1)在MyBatis中,是使用RowBounds对象进行分页的,它是针对ResultSet结果集执行的逻辑分页,而不是物理分页。

(2)另外,我们可以在SQL内,直接书写带有物理分页的参数来完成物理分页功能,也可以使用第三方的分页插件PageHelper来完成物理分页。

 


  
  1. <!-- SQL物理分页 -->
  2. <select id="queryStudentsBySql" parameterType="map" resultMap="studentmapper">
  3. select * from student limit #{start} , #{end}
  4. </select>

(3)分页插件的原理(物理分页):就是使用MyBatis提供的插件接口,来实现自定义插件。在自定义插件的拦截方法内,拦截待执行的SQL,然后根据设置的分页参数 重写SQL ,生成带有分页语句的SQL,最终执行的是重写之后的SQL,从而实现分页。 举例:
select * from student,分页插件拦截SQL后 重写SQL为:select t.* from (select * from student)t limit 0,10;

8、MyBatis的动态SQL是什么?主要标签有哪些?

  1. 原生的JDBC的方法,在组合SQL语句的时候需要手动去拼接,稍微不注意就会少了一个空格、一个标点符号等,都会导致系统错误。MyBatis的动态SQL,就是为了解决这种问题而产生的。MyBatis的动态SQL语句是基于OGNL表达式的,方便在SQL语句中实现某些逻辑;也可以使用一些标签灵活的组合成我们想要的SQL语句,从而提高开发效率。
  2. MyBatis的动态SQL标签,主要有以下几类:(1) if 标签,配合 test 标签用来做简单的条件判断。(2)choose 标签,配合 when、otherwise 标签,相当于Java语言中的switch...case语句实现分支选择功能。(3)trim 标签,对包含的内容加上前缀 prefix、或者后缀 suffix。 (4)where 标签,主要是用来简化SQL语句中where条件判断的,能智能的处理 and、or,不用担心有多余的 and 或者 or 导致语法错误。(5)set 标签,主要用来做数据update的时候。(6)foreach 标签,主要用在 Mybatis in 语句中。

9、MyBatis用到了哪些设计模式?

  1. 日志模块:代理模式、适配器模式
  2. 数据源模块:代理模式、工厂模式
  3. 缓存模块:装饰器模式
  4. 初始化阶段:建造者模式
  5. 代理阶段:策略模式
  6. 数据读写阶段:模板模式
  7. 插件化开发:责任链模式

10、MyBatis中都有哪些Executor执行器?它们之间的区别是什么?

  1. BaseExecutor:基础抽象类,实现了Executor接口的大部分方法,主要提供了缓存管理和事务管理的能力,使用了模板模式,doUpdate、doQuery、doQueryCursor 等方法的具体实现交给不同的子类去实现。
  2. CachingExecutor:直接实现Executor接口,使用装饰器模式提供二级缓存能力。先从二级缓存中查询,缓存没有命中再从数据库中查询,最后将结果添加到缓存中再返回给用户。如果在xml文件中配置了<cache/>节点,则会创建 CachingExecutor。
  3. BatchExecutor:BaseExecutor的具体子类实现,在doUpdate方法中,提供批量执行多条SQL语句的能力。
  4. SimpleExecutor:BaseExecutor的具体子类实现,且为默认配置,在doQuery方法中使用PrepareStatement对象访问数据库,每次访问都要创建新的PrepareStatement对象。
  5. ReuseExecutor:BaseExecutor的具体子类实现,与SimpleExecutor不同的是,在doQuery方法中,使用预编译PrepareStatement对象访问数据库,访问时,会重用缓存中的statement对象,而不是每次都创建新的PrepareStatement。

11、MyBatis的Xml映射文件和MyBatis内部数据结构之间的映射关系?

  1. MyBatis会将所有Xml配置信息都封装到 All-In-One 重量级对象Configuration类对象中。
  2. 在Xml映射文件中,<resultMap>标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。
  3. 每一个<select>、<insert>、<update>、<delete>标签均会被解析为MappedStatement对象,标签内的SQL都会被解析为BoundSql 对象,然后再和数据库打交道。

 

99、参考

(1)https://blog.csdn.net/cmm0401/article/details/106875187

(2)https://zhuanlan.zhihu.com/p/269260414

(3)https://blog.csdn.net/cmm0401/article/details/111768743

(4)MyBatis面试题集合:https://thinkwon.blog.csdn.net/article/details/101292950

 


转载:https://blog.csdn.net/cmm0401/article/details/116171937
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场