5. MyBatis 面试题集锦

描述Mybatis三阶段

MyBatis 的好处是什么

1)MyBatis 把 sql 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写, 给程序的维护带来了很大便利。(维护)

2)MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 Java Bean 对象,大大简化了 Java 数据库编程的重复工作。 (简化)

3)因为 MyBatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的 特点灵活控制 sql 语句,因此能够实现比 Hibernate 等全自动 orm 框架更高的查 询效率,能够完成复杂查询。(灵活)

为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?

Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集 合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询 关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映 射工具。

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

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

resultType 和 resultMap 的区别?

resultType 和 resultMap 都是表示数据库表与 pojo 之间的映射规则的。类的名字和数据 库相同时,可以直接设置 resultType 参数为 Pojo 类。若不同或者有关联查询,需要设置 resultMap 将结果名字和 Pojo 名字进行转换;从编程的最佳实践来讲,强制使用resultMap, 不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义; 见《Java 开发手册 1.5》之 5.4.3;

在 MyBatis 中如何在 mapper 接口中传入多个参数

建议不要用 Map 作为 mapper 的输入和输出,不利于代码的可读性和可维护性;见《Java 开发手册 1.5》之 5.4.6;

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

向 sql 语句中传递的可变参数,分为预编译#{}和传值${}两种

  • 预编译 #{}:将传入的数据都当成一个字符串,会对自动传入的数据加一个单引号,能 够很大程度防止 sql 注入;
  • 传值 :传入的数据直接显示生成在 s q l 中,无法防止 s q l 注入;适用场景: ∗ ∗ 动态报表,表名、选取的列是动态的, o r d e r b y 和 i n 操作,可以考虑使用 {}:传入的数据直接显示生成在 sql 中,无法防止 sql 注入;适用场景:**动态报表, 表名、选取的列是动态的,order by 和 in 操作, 可以考虑使用 :传入的数据直接显示生成在sql中,无法防止sql注入;适用场景:动态报表,表名、选取的列是动态的,orderbyin操作,可以考虑使用**

通过 Mybatis 怎么样进行批量插入操作

  1. 通过 foreach 动态拼装 SQL 语句
  2. 使用 BATCH 类型的 excutor;

而且这两种方式都能返回数据库主键字段;

Mybatis 怎么样实现关联查询

  1. 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。使用 join 查询,一部分列是 A 对象的属性值,另外一部分列是关联对象 B 的属性值,好处是只发一个 sql查询,就可以把主对象和其关联对象查出来。
  2. 嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型,在关联标签中配置 select、fetchType 这样的属性实现,是通过发送多段 SQL 实现的;

什么是 N+1 问题,怎么解决?

嵌套查询会导致“N+1 查询问题”,导致该问题产生的原因:

  1. 你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。
  2. 对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。这通常不是期望的。 解决“N+1 查询问题”的办法就是开启懒加载、按需加载数据,开启懒加载配置: 在节点上配置“fetchType=lazy”,在 MyBatis 核心配置文件中加入如下配置:

<!-- 开启懒加载 ,当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每
种属性将会按需要加载。默认:true -->
<setting name="aggressiveLazyLoading" value="false" />

什么是一级缓存?怎么理解二级缓存?

一级缓存存在于 SqlSession 的生命周期中,在同一个 SqlSession 中查询时使用。二级缓存 也叫应用缓存,存在于 SqlSessionFactory 的生命周期中,可以理解为跨 sqlSession;二级缓存是 以 namespace 为单位的,不同 namespace 下的操作互不影响。在项目中为了避免脏读的问题, 建议不适用二级缓存。

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

1)Mybatis 使用 RowBounds 对象进行分页,也可以直接编写 sql 实现分页,也可以使用Mybatis 的分页插件。
2)分页插件的原理:实现 Mybatis 提供的接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql。
举例:select * from student,拦截 sql 后重写为:

select t. from (select * from student)t limit 0,10*

简述 Mybatis 的插件运行原理,以及如何编写一个插件?

1)Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件,Mybatis 通过动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler 的 invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
2)实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id

看情况

  • 不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;

  • 如果没有配置 namespace, 那么 id 不能重复;

    毕竟 namespace 不是必须的,只是最佳实践而已。原因就是 namespace+id 是作为 Map的 key 使用的,如果没有 namespace,就剩下 id, 那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同, namespace+id 自然也就不同

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

Mybatis 有三种基本的 Executor 执行器,SimpleExecutor、ReuseExecutor、BatchExecutor

1) SimpleExecutor:Mybatis 的默认执行器,每执行一次 update 或 select,就开启一个 Statement 对 象,用完立刻关闭 Statement 对象。

2)ReuseExecutor:执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放 置于 Map

3)BatchExecutor:完成批处理。

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

  1. Mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 相同;(方法->id)
  2. Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同;(入参)
  3. Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同;(结果)
  4. Mapper.xml 文件中的namespace 即是 mapper 接口的类路径。

为什么使用 mapper 接口就能对数据库进行访问?

表面上我们在使用 mapper 接口访问数据库,实际 MyBatis 通过动态代理生成了 mapper 接 口的实现类,通过这个动态生成的实现类,将数据库的访问请求转发给 SqlSession。转发过程中 需要实现三个翻译:

  1. 通过 sql 语句的类型和接口方法的返回参数确定调用 SqlSession 的哪个方法;
  2. 将 sql 语句在 MyBatis 中的两维坐标作为第一个参数,传入 SqlSession 的方法;
  3. 将接口传入的参数封装成 map 后作为第二个参数,传入 SqlSession 的方法;

请求转发给 SqlSession 后实现对数据库的访问操作

怎么样获取主键?

1. 通过 insert/update 标签相关属性

属性描述
useGeneratedKeys(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系 数据库管理系统的自动递增字段),默认值:false。
keyProperty( 仅 对 insert 和 update 有 用 ) 唯 一 标 记 一 个 属 性 , MyBatis 会 通 过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键 值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
<insert id="insert1" parameterType="TUser" useGeneratedKeys="true"  keyProperty="id">
   insert into t_user (id, userName, realName,
   sex, mobile,
   email,
   note, position_id)
   values (#{id,jdbcType=INTEGER},
   #{userName,jdbcType=VARCHAR},
   #{realName,jdbcType=VARCHAR},
   #{sex,jdbcType=TINYINT}, #{mobile,jdbcType=VARCHAR},
   #{email,jdbcType=VARCHAR},
   #{note,jdbcType=VARCHAR},
   #{position.id,jdbcType=INTEGER})
</insert>

2. 通过 selectKey 元素

属性描述
keyPropertyselectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号 分隔的属性名称列表
resultType结果的类型。MyBatis 通常可以推算出来,但是为了更加确定写上也不会有什么问题。 MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的 列,则可以使用一个包含期望属性的 Object
order(对插入而言)这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先选择主键,设 置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后获 取主键字段;mysql 数据库自增长的方式 order 设置为 After,oracle 数据库通过 sequnce 获取主键 order

oracle

<selectKey keyProperty=“id” order= "Before" resultType="int">
	select SEQ_ID.nextval from dual
</selectKey>

mysql

<selectKey keyProperty="id" order="AFTER" resultType="int">
	select LAST_INSERT_ID()
</selectKey>

动态sql的where后无参数的处理

foreach,if元素

where, trim,set元素

使用where 1 = 1 and, 不优雅

使用where子元素处理, 比较优雅, 没有条件时候就没有where字句

动态sql的set元素

trim元素是其他set,where的抽象

集合的处理

单条执行一个sql: 数组使用 collection = “array”, 集合的collection=“list”,

批量执行多条sql: 批量操作, 指定执行器的操作类型的batch, 不使用batch方式就会使用多个连接去执行, 生成多个事务, 所以使用batch就会使用同一个事务, 需要自己提交

代码生成器

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE generatorConfiguration PUBLIC   
"-//mybatis//DTD MyBatis Generator Configuration 1.0//EN"  
 "http://mybatis/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
   <!-- 引入配置文件 -->
   <properties resource="db.properties" />


   <classPathEntry location="${class_path}" />
   <context id="context1" targetRuntime="MyBatis3">
      <commentGenerator>
         <!-- 是否去除自动生成的注释 true:是 : false:否 -->
         <property name="suppressAllComments" value="true" />
      </commentGenerator>
         <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
      <jdbcConnection driverClass="${jdbc_driver}"
         connectionURL="${jdbc_url}" userId="${jdbc_username}" password="${jdbc_password}" />
      <javaModelGenerator targetPackage="com.enjoylearning.mybatis.entity"
         targetProject="${project_src}" />
      <sqlMapGenerator targetPackage="." targetProject="${project_mapper_xml}" />
      <javaClientGenerator targetPackage="com.enjoylearning.mybatis.mapper"
         targetProject="${project_src}" type="XMLMAPPER" />
      <!-- shema 数据库 tableName表明 -->



      
      <table schema="${jdbc_username}" tableName="t_role"
          enableCountByExample="false"
         enableUpdateByExample="false" enableDeleteByExample="false"
         enableSelectByExample="false" selectByExampleQueryId="false">
      </table>
      
   <!--   <table schema="${jdbc_username}" tableName="t_user"
          enableCountByExample="false"
         enableUpdateByExample="false" enableDeleteByExample="false"
         enableSelectByExample="false" selectByExampleQueryId="false">
      </table>

      <table schema="${jdbc_username}" tableName="t_user_role"
          enableCountByExample="false"
         enableUpdateByExample="false" enableDeleteByExample="false"
         enableSelectByExample="false" selectByExampleQueryId="false">
      </table> -->

   </context>
</generatorConfiguration>

三种运行方式

  1. 使用maven插件
mvn mybatis-generator.generate
  1. 使用main运行

  2. 使用java -jar 运行

   java -jar mybatis-generator-core-xxx.jar -configgile generatorConfig.xml

详细描述从数据库连接池获取连接资源的过程

连接池数据结构和核心算法

重点理解学习

Mybatis的缓存功能使用HashMap实现会不会出现并发安全的问题?

使用了SynchronizedCache装饰器, 装饰器的所有的方法都加了synchronized关键字, 实现了同步操作

Mybatis内部的分页组件: RowBounds

更多推荐

5. MyBatis 面试题集锦