MyBatis 面试题

1. MyBatis 是什么

MyBatis 是一个持久层框架,它支持定制化 sql、存储过程以及高级映射;内部封装了 JDBC,开发时只需要关注 sql 语句本身,不需要花费精力去处理加载驱动创建连接等繁琐的过程;通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 Java 对象和 statement 中的 sql 动态参数进行映射,生成最终执行的 sql 语句,最后由 MyBatis 框架执行 sql,并将结果映射为 Java 对象返回;

2. MyBatis 的优缺点

优点

  1. 基于 sql 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成影响,sql 写在 xml 里,解除 sql 与程序代码的耦合;
  2. 支持编写动态 sql 语句,(在 mapper.xml 映射文件里,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能);
  3. 与 JDBC 相比,减少了 50% 以上的代码量,不需要手动开关连接;
  4. 提供映射标签,支持对象与数据库的 ORM(object relation mapping) 字段关系映射;

缺点

  1. sql 语句的编写工作量较大,尤其当字段多、关联表多的时候;
  2. sql 语句依赖于数据库,导致数据库移植性差;

3. #{} 和 ${} 占位符的区别是什么

两者都可以进行动态 sql 拼接,使用 $ 符比使用 # 号多了一对单引号,# 号会进行预编译,而且会进行类型匹配,参数是在编译之后填充进去的,不需要单引号;而 $ 符不会进行数据类型匹配,只是单纯的进行字符串拼接,所以要手动加上单引号;

# 号重要作用防止 sql 注入,变量传递时必须使用 # 号,既可以提高 sql 的执行效率,又可以防止 sql 注入的问题;

SELECT * FROM testIndex WHERE NAME = '${param}';
SELECT * FROM testIndex WHERE NAME = #{param};

4. Hibernate 和 MyBatis 的区别

相同点:都是对 JDBC 的封装、都是持久层框架;

映射关系不同:MyBatis 是一个半自动化映射的框架,配置 Java 对象与 sql 语句执行结果的对应关系,多表关联关系配置简单;Hibernate 是一个全表映射的框架,配置 Java 对象与数据库表的对应关系,多表关联关系配置复杂;

sql 优化和移植性不同:MyBatis 需要手动编写 sql,开发工作量相对较大,但优化容易;Hibernate 支持多种数据库,代码开发量少,但 sql 优化困难;

5. MyBatis 编程步骤是什么样的

  • 使用 mybatis 框架中的 Resource 类,加载全局配置文件
  • 创建 SqlSessionFactory(会话工厂)
  • 通过 SqlSessionFactory 创建 SqlSession(会话对象)
  • 通过 sqlsession 执行数据库操作
  • 调用 sqlsession.commit() 提交事务
  • 调用 sqlsession.close() 关闭会话

6. MyBatis 的功能框架是怎样的 / 谈谈你对 MyBatis 的理解

API 接口层:提供给外部使用的接口 API,开发人员通过这些 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理;

数据处理层:负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要是根据调用的请求完成一次数据库操作;

基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理;

引导层:MyBatis 启动时需要的配置信息,提供了两种配置方式:基于 XML 和基于 Java API 方式;

7. MyBatis 的工作原理

  1. 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境、mapper 映射文件的全路径名等信息;
  2. 加载映射文件:该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 的配置文件中加载。mybatis 的配置文件可以加载多个映射文件,每个文件对应一张数据库表;
  3. 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory;
  4. 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法;
  5. Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句;
  6. MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装;
  7. 输入参数映射:输入参数类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型;
  8. 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型;

Configuration:核心配置类:存放全局配置文件解析出来的内容;

MappedStatement:映射配置类:存放映射文件解析出来的内容;

XMLConfigBuilder:专门解析全局配置文件的类;

XMLMapperBuilder:专门解析映射文件的类;

8. 什么是 MyBatis 的接口绑定

MyBatis 的接口绑定,就是把接口里面的方法和 SQL 语句绑定,以及 SQL 执行的结果与方法返回值进行转换匹配;接口绑定有两种方式:

  1. 注解绑定:就是在接口的方法上加上@Select、@Update 等注解,里面包含 sql 语句来绑定;
  2. xml 里写 sql 来绑定:必须要指定映射文件里的 namespace 值为接口的全限定名

9. MyBatis 的 mapper 接口调用时有哪些要求

  • mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 值必须相同;
  • mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 类型相同;
  • mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 类型相同;
  • mapper.xml 文件中的 namespace 为 mapper 接口的全限定名;

10. 在 mapper 中如何传递多个参数

  1. 顺序传参法:#{} 里面的数字代表传入参数的顺序,即 #{0} 代表第一个参数,#{1} 代表第二个参数,这种方法不建议使用,一旦顺序调整,容易出错;
  2. @Param 注解传参法:#{} 里面的名称对应的是注解 @Param 括号里面修饰的名称,这种方法在参数不多的情况还是比较直观的;
  3. Map 传参法:#{} 里面的名称对应的是 Map 里面的 key 名称,这种方法适合传递多个参数;
  4. Java Bean 传参法:#{} 里面的名称对应的是实体类里面的成员属性,这种方法比较直观,需要建一个实体类,扩展起来不容易,需要加属性,但代码可读性强,业务逻辑处理方便;

11. mapper 接口的工作原理是什么

mapper 接口的全限定名就是映射文件中 namespace 的值,接口的方法名就是映射文件中定义的每个 sql 的 id 值,接口方法内的参数,就是传递给 sql 的参数;

mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 mapper 接口生成代理对象,代理对象会拦截接口方法,转而执行 MappedStatement 代表的 sql,然后将 sql 执行结果返回;

mapper 接口里的方法,是不能重载的,因为是全限定名+方法名的保存和寻找策略,接口全限定名+方法名拼接字符串作为 key,可唯一定位一个 MappedStatement

mapper 接口里的方法,可以被重载,但是接口对应的 xml 文件里的 id 值不允许重复;需要满足以下两个条件:仅有一个无参方法和一个有参方法;多个有参方法时,参数数量必须一致;

List<Student> getAllStu();
List<Student> getAllStu(@Param("id") Integer id);
// List<Student> getAllStu(@Param("id") Integer id, @Param("name") String name);

12. MyBatis 如何执行批量插入

Mybatis 执行批量操作,主要有两种方式:使用 foreach 标签、使用 ExecutorType.BATCH;

foreach 的用法:

foreach 可以在 sql 语句中迭代一个集合,foreach 标签的属性主要有 item、index、collection、open、separator、close;

  • item 表示集合中每一个元素进行迭代时的别名;
  • index 指定一个名字,用于表示在迭代过程中,每次迭代到的位置;
  • open 表示该语句以什么开始,常用“(”;
  • separator 表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;
  • close 表示以什么结束,常用“)”;
  • collection 属性,该属性必须指定,在不同情况下,该属性的值是不一样的,主要有以下 3 种:
  1. 如果传入的是单参数且参数类型是一个 List 时,collection 属性值为 list;
  2. 如果传入的是单参数且参数类型是一个 array 数组时,collection 的属性值为 array;
  3. 如果传入的参数是多个时,可以把它们封装成一个 map,collection 的属性值为 map 的键;

13. preparedStatement 和 statement 区别

preparedStatement 和 statement 都是用于执行 sql 语句的接口

statement 是最基本的 sql 语句执行接口,由于每次执行 sql 语句都需要将 sql 语句发送给数据库服务器进行解析编译,因此对于重复执行的 sql 语句会产生性能问题

preparedStatement 接口可以提高性能,它预编译了 sql 语句并将编译结果缓存起来,这样在多次执行相同的 sql 语句时,不需要重新编译,从而提高执行效率。此外,preparedStatement 还可以防止 sql 注入,因为它会将传入的参数转义后再执行 sql;

14. 谈谈对 SqlSessionFactory 的理解

SqlSessionFactory 是 MyBatis 中的一个核心 API。目的是创建 SqlSession 对象,SqlSessionFactory 应该是单例。SqlSessionFactory 对象的创建是通过 SqlSessionFactoryBuilder 来实现。在 SqlSessionFactoryBuilder 中既完成了 SqlSessionFactory 对象的创建,也完成了全局配置文件和相关映射文件的加载和解析。而且涉及到了两种涉及模式:工厂模式,建造者模式;

15. 谈谈对 SqlSession 的理解

SqlSession 是 MyBatis 中的一个核心 API,SqlSession 对象中包含了执行 SQL 语句的所有方法,该对象的获取需要通过 SqlSessionFactory 中的 openSession() 方法来实现。当一个新的会话到来时,我们需要新建一个 SqlSession 对象来处理,当一个会话结束后我们需要关闭相关的会话资源。处理请求的方式:

  1. 通过相关的增删改查 API 直接处理;
  2. 通过 getMapper(xxx.class) 方法来获取相关的 mapper 接口的代理对象来处理;

openSession() 执行逻辑:创建事务对象、创建执行器对象(CachingExecutor)和 DefaultSqlSession 对象;

16. 谈谈对 MyBatis 中缓存的理解

缓存的作用:降低数据库的访问压力,提高服务器的响应速度;

缓存的设计:通过装饰器模式实现缓存功能的扩展;

缓存的应用:一级缓存和二级缓存(为什么先走二级缓存,再走一级缓存)

因为二级缓存的作用域是 SqlSessionFactory 级别的,一级缓存的作用域是 SqlSession 级别的;一级缓存的作用域太小了,有时候直接从二级缓存中就找到了,就不用走一级缓存了;

MyBatis 缓存的设计就是 HashMap,其中 key 是 CacheKey 对象,value 是查询的数据;CacheKey 对象由 statementId、分页参数(起始值、每页数量)、sql 语句、传递的参数值和当前环境的值,这几部分组成,比较的是 CacheKey 的 hashCode 值是否相等;

一级缓存是指,在一次事务中,多次查询同一个 SQL 语句,MyBatis 是有缓存的,它只会帮我们查第一次。二级缓存的范围更大,是指多次查询同一个 SQL 语句,不止是一个事务,多次调用同一个接口,如果二级缓存生效,则数据库只会去查一次;

17. MyBatis 是如何进行分页的

mybatis 的分页方式一共有三种:

  1. 直接在 SQL 内写带有分页的参数完成分页(手写 limit 关键字);
  2. 使用 mybatis 提供的 RowBounds 对象进行分页,它是对 ResultSet 结果集执行的内存分页
  3. 使用 Pagehelper 分页插件;Pagehelper 插件的原理是使用拦截器拦截 SQL 语句,为 SQL 语句添加 limit 关键字进行分页查询,以及 count 语句来查询总数;

18. Spring 中如何解决 DefaultSqlSession 的数据安全问题

DefaultSqlSession 是线程不安全的,也就意味着我们不能把 DefaultSqISession 声明在成员变量中。在 Spring 中提供了一个 SqlSessionTemplate 来实现 SqlSession 的相关定义,在 SqlSessionTemplate 中的每个方法中都通过 SqlSessionProxy 来操作。SqlSessionProxy 是一个动态代理对象,在动态代理对象中通过方法级别的 DefaultSqlSession 来实现相关的数据库操作;

19. 谈谈 MyBatis 延迟加载的理解

延迟加载:又称懒加载,延迟加载只存在于数据库表的关联查询中,单表查询没有延迟加载的功能;比如说:查询用户和订单信息,如果我们只需要用户的信息,而不需要用户对应的订单信息,这时就可以使用延迟加载机制来处理;延迟加载是基于嵌套查询来实现的;

  1. 需要开启延迟加载在 MyBatis 的配置⽂件中用 setting 标签打开延迟加载,就是将 lazyLoadingEnabled 设置为 true;
  2. 需要配置多表关联association 是一对一的关联配置,collection 是一对多的关联配置;

延迟加载带来的问题:如果延迟加载的表数据太多,此时会产生 N+1 问题,主信息加载一次,而从信息会根据主信息传递过来的条件,去查询从表多次;

20. 谈谈对 MyBatis 中插件原理的理解

MyBatis 插件设计的目的:方便开发人员实现对 MyBatis 功能的增强;默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:Executor、ParameterHandler、ResultSetHandler、StatementHandler;

如果要实现自定义的拦截器:

  1. 创建自定义的 java 类,通过 interceptors 注解来定义相关的方法签名;
  2. 需要在对应的配置文件中通过 plugins 标签来注册自定义的拦截器;

21. 当属性名和字段名不一致的情况怎么办

  1. 通过在查询的 sql 语句中定义字段名的别名,让字段名的别名和属性名一致;
  2. 通过自定义<resultMap>标签来设置属性名和字段名的映射关系;
<resultMap type="" id="">
	<!- property 为实体类属性名,column 为数据表中的字段名–>
	<result property ="userName" column ="user_name"/>
</reslutMap>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值