Mybatis面试题汇总

一、使用方面的问题

1.什么是Mybatis?说一下Mybatis:

MyBatis是一个半ORM框架,它内部封装了JDBC,开发的时候只需要关注SQL语句本身,不需要花太多精力去处理原生的JDBC那一套流程,比如 加载驱动、创建connection连接、创建statement、创建SqlSesssionFactory、创建SQLSession等一系列比较繁杂且重复性比较高的过程。

MyBatis可以使用 SQL映射文件XML的方式 或者 注解的方式 来配置映射信息,将Java对象映射成数据库当中的记录,或者将数据库当中的记录反映射成Java对象。

MyBatis也支持动态SQL,它是通过Java对象和statement对象当中SQL的动态参数进行映射从而生成最终要执行的SQL语句,最后由MyBatis框架执行SQL并将结果映射为Java对象返回的。

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

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

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

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

主要有两种解决方式:

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

(2)第二种是使用在SQL中定义列的别名,将列的别名与实体类对象的属性名一一对应起来。

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接口开发时有哪些要求?

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

二、源码方面的问题

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

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

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

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

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

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

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

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

img

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

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

5、MyBatis的缓存是什么?

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

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

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

7、MyBatis的分页原理是什么

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

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

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

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

原生的JDBC的方法,在组合SQL语句的时候需要手动去拼接,稍微不注意就会少了一个空格、一个标点符号等,都会导致系统错误。MyBatis的动态SQL,就是为了解决这种问题而产生的。MyBatis的动态SQL语句是基于OGNL表达式的,方便在SQL语句中实现某些逻辑;也可以使用一些标签灵活的组合成我们想要的SQL语句,从而提高开发效率。
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执行器?它们之间的区别是什么?

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

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

    img

img

img

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

12、MyBatis一二级缓存的工作原理

img img
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值