《2021最新Java面试题全集-2021年第二版》不断更新完善!

    

第十三章 MyBatis

1:MyBatis #{} ${}的区别是什么?

1#{}是预编译处理,${}是字符串替换。

2Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement set 方法来赋值;

3Mybatis 在处理${}时,就是把时,就是把${}替换成变量的值。

4)使用#{}可以有效的防止 SQL 注入,提高系统安全性。

 

 

2:解释一下MyBatis中命名空间(namespace)的作用

在大型项目中,可能存在大量的SQL语句,这时候为每个SQL语句起一个唯一的标识(ID)就变得并不容易了。为了解决这个问题,在MyBatis中,可以为每个映射文件起一个唯一的命名空间,这样定义在这个映射文件中的每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的,即使在不同映射文件中的语句ID相同,也不会再产生冲突了。

 

3:MyBatis中的动态SQL是什么意思?

对于一些复杂的查询,我们可能会指定多个查询条件,但是这些条件可能存在也可能不存在,这就需要根据用户指定的条件动态生成SQL语句。

如果不使用持久层框架我们可能需要自己拼装SQL语句,还好MyBatis提供了动态SQL的功能来解决这个问题。MyBatis中用于实现动态SQL的元素主要有:

- if

- choose / when / otherwise

- trim

- where

- set

- foreach

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

 

4:MyBatis 有几种分页方式?

1)数组分页:

进行数据库查询操作时,获取到数据库中所有满足条件的记录,保存在应用的临时数组中,再通过ListsubList方法,获取到满足条件的所有记录。

2Sql分页:

通过sql语句实现分页

3)拦截器分页:

利用interceptor来拼接sql,实现和sql一样的功能

4RowBounds实现分页:

通过RowBounds实现分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。只是数组分页需要我们自己去实现分页逻辑,这里更加简化而已。

 

5:RowBounds 是一次性查询全部结果吗?为什么?

是的,RowBounds实现分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。

    Mysql中可以使用limit语句,但limit并不是标准SQL中的,如果是其它的数据库,则需要使用其它语句。MyBatis提供了RowBounds类,用于实现分页查询。RowBounds中有两个数字,offsetlimit

RowBounds在处理分页时,只是简单的把offset之前的数据都skip掉,超过limit之后的数据不取出。

简单点说道,就是先把数据全部查询到ResultSet,然后从ResultSet中取出offsetlimit之间的数据,这就实现了分页查询。

 

6:MyBatis 逻辑分页和物理分页的区别是什么?

物理分页速度上并不一定快于逻辑分页,逻辑分页速度上也并不一定快于物理分页。

物理分页总是优于逻辑分页:没有必要将属于数据库端的压力加诸到应用端来,就算速度上存在优势,然而其它性能上的优点足以弥补这个缺点。

 

7:MyBatis 是否支持延迟加载?延迟加载的原理是什么?

MyBatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在MyBatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false

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

 

8:什么是 MyBatis 的接口绑定?有哪些实现方式?

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

接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上 @Select@Update 等注解,里面包含 Sql 语句来绑定;另外一种就是通过 xml 里面写 SQL 来绑定, 在这种情况下,要指定 xml 映射文件里面的 namespace 必须 为接口的全路径名。

Sql 语句比较简单时候,用注解绑定, SQL 语句比较复杂 时候, xml 绑定,一般用 xml 绑定的比较多。

 

9:使用 MyBatis mapper 接口调用时有哪些要求?

1Mapper 接口方法名和 mapper.xml 中定义的每个 sql id 相同

2Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql parameterType 的类型相同

3Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql resultType 的类型相同

4Mapper.xml 文件中的 namespace 即是 mapper 接口的类路径。

 

10:说一下 MyBatis 的一级缓存和二级缓存?

一级缓存:

基于 PerpetualCache HashMap 本地缓存,其存储作用域为 Session,当 Session flush close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。

二级缓存:

与一级缓存其机制相同,默认也是采用 PerpetualCacheHashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置

对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear

 

11:MyBatis Hibernate 的区别有哪些?

1MyBatisHibernate不同,它不是一个完全的、全自动化ORM框架,因为MyBatis需要程序员自己编写Sql语句。

2MyBatis直接编写原生态sql,可以严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是MyBatis无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套sql映射文件,工作量大。

3Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用Hibernate开发可以节省很多代码,提高效率。

 

12:MyBatis 有哪些执行器(Executor)?

MyBatis有三种基本的执行器(Executor):

(1)  SimpleExecutor:每执行一次updateselect,就开启一个Statement对象,用完立刻关闭Statement对象。

(2)  ReuseExecutor:执行updateselect,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。

(3)  BatchExecutor:执行update(没有selectJDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同

 

13:MyBatis 分页插件的实现原理是什么?

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

 

14:MyBatis 如何编写一个自定义插件?

MyBatis自定义插件针对MyBatis四大对象(ExecutorStatementHandler ParameterHandler ResultSetHandler )进行拦截,每当执行这 4 接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler invoke() 方法,当然,只会拦截那些你指定需要拦截的方法。

Executor:拦截执行器的方法(log记录)

StatementHandler :拦截Sql语法构建的处理

ParameterHandler :拦截参数的处理

ResultSetHandler :拦截结果集的处理

编写插件:实现 Mybatis Interceptor 接口并复写 intercept()方法,然后在给 插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

 

15:什么是 MyBatis 的接口绑定,有什么好处?

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

 

16:Mybatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别?

能,Mybatis 不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询。

多对一查询,其实就是一对一查询,只需要把 selectOne()修改为 selectList()即可;多对多查询,其实就是一对多查询,只需要把 selectOne()修改为 selectList()即可。

关联对象查询,有两种实现方式,一种是单独发送一个 sql 去查询关联对象,赋给主对象,然后返回主对象。

另一种是使用嵌套查询,嵌套查询的含义为使用 join 查询,一部分列是 A 对象的属性值,另外一部分列是关联对象 B 的属性值,好处是只发一个 sql 查询,就可以把主对象和其关联对象查出来。