MyBatis常见面试题汇总(超详细回答)

目录

1.什么是Mybatis?

2.Mybatis的优缺点?

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

4.xml 映射文件中有哪些标签?

5.模糊查询 like 语句该怎么写?

6.Mapper 接口的工作原理是什么?Mapper 接口里的方法,参数不同时,方法能重载吗?

7.MyBatis 是如何进行分页的?分页插件的原理是什么?

8.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

9.如何执行批量插入?能返回主键id吗

10.MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?

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

12.MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?

13.MyBatis 的 xml 映射文件中,不同的 xml 映射文件的id 是否可以重复?

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

15.MyBatis 中如何指定使用哪一种 Executor 执行器?

16.MyBatis 是否可以映射 Enum 枚举类?

17.简述 MyBatis 的 xml 映射文件和 MyBatis 内部数据结构之间的映射关系?

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

19.Mybatis 的一级、二级缓存?

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


1.什么是Mybatis?

Mybatis是一种流行的Java对象关系映射(ORM)框架,它将Java对象映射到关系型数据库中的表格。它提供了一种简单的方式来编写SQL语句并将其映射到Java对象,同时避免了很多传统JDBC的冗长代码。在使用Mybatis时,开发人员可以编写简洁的SQL语句,并且可以使用动态SQL和参数映射来进行高效和灵活的数据操作。

除了ORM之外,Mybatis还提供了许多高级功能,如缓存机制、批量操作、分页等等。它可以与多种数据库(如MySQL、Oracle、SQL Server等)和Web框架(如Spring、Spring Boot等)无缝集成,并被广泛应用于各种规模的应用程序中。

2.Mybatis的优缺点?

Mybatis的优点:

  1. 灵活性强:Mybatis允许开发人员编写自定义SQL语句,能够根据项目需求进行灵活配置和扩展,同时可以避免框架的约束和限制。
  2. 可读性好:Mybatis使用简洁的XML或注解配置方式,可以清晰地表达SQL语句和数据映射关系,提高代码的可读性和可维护性。
  3. 性能高:Mybatis提供了高效的缓存机制,能够有效地减少数据库操作的次数,并支持批量操作和分页查询等功能,能够提高应用程序的性能。
  4. 易于集成:Mybatis可以与多种数据库和Web框架无缝集成,能够适应不同的应用场景,同时也有完善的文档和社区支持。

Mybatis的缺点:

  1. 学习成本较高:相比于其他ORM框架,Mybatis需要开发人员熟悉SQL语句的编写和调优,需要一定的学习成本。
  2. 配置较为繁琐:Mybatis的配置文件较多,需要开发人员仔细配置,否则容易出现错误。
  3. SQL语句调试困难:Mybatis将SQL语句和Java代码分离,当SQL语句出现问题时,调试起来相对困难。
  4. 不适合小型项目:对于小型项目而言,Mybatis的优势可能不够明显,反而会增加项目的开发成本和复杂度。

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

#{}使用预编译的方式来处理SQL语句中的参数,将传入的参数值以安全的方式替换掉占位符。在SQL语句执行前,会先将#{}替换为一个问号占位符,然后使用PreparedStatement进行预编译,最后将实际的参数值设置到预编译语句中。使用#{}可以有效地防止SQL注入等安全问题,同时也可以避免一些数据类型转换的问题。

${}则是直接将参数值替换到SQL语句中。在SQL语句执行前,会直接将${}替换为对应的参数值,这种方式的好处是可以直接拼接字符串,但也带来了一些安全问题。使用${}时需要开发人员自行保证参数的合法性,否则可能会出现SQL注入等安全问题。

   例如:

SELECT * FROM user WHERE name = '${name}'

如果$name的值是 "admin' OR '1'='1",则该SQL语句会查询出所有用户的数据,而不是仅查询用户名为"admin"的用户数据,造成了安全风险。

4.xml 映射文件中有哪些标签?

  1. CRUD操作标签:包括selectinsertupdatedelete等标签,用于定义对数据库的增、删、改、查操作。这些标签都包括id、parameterType、resultType等属性,用于指定SQL语句的ID、参数类型和返回结果类型等信息。
  2. 结果集映射标签:包括标签,用于定义Java对象和数据库表之间的映射关系,可以将查询结果集映射为Java对象的属性。标签用于定义映射规则,标签用于定义单个属性的映射关系。
  3. SQL片段标签:包括标签,用于定义可重用的SQL代码片段。标签用于定义SQL语句的代码片段,可以在其他SQL语句中使用,标签用于将其他XML片段引入到当前XML文件中,可以用于复用其他XML片段中定义的SQL语句。
  4. 动态SQL标签:包括等标签,用于动态生成SQL语句中的条件判断、循环、赋值等操作。这些标签可以根据参数值动态生成SQL语句,提高SQL语句的复用性和可维护性。

5.模糊查询 like 语句该怎么写?

下面是两种常见的模糊查询方式:

1.使用占位符(%)进行模糊查询

 SQL语句示例:

SELECT * FROM table WHERE column LIKE '%keyword%';

XML映射文件示例:

在XML映射文件和注解中,可以使用CONCAT函数将占位符与参数进行拼接,生成完整的LIKE语句。

2.使用$符号进行模糊查询

 SQL语句示例:

SELECT * FROM table WHERE column LIKE '%${keyword}%';

XML映射文件示例:

在XML映射文件和注解中,可以使用${}占位符将参数直接拼接到SQL语句中,生成完整的LIKE语句。需要注意的是,使用${}方式进行模糊查询存在SQL注入的风险,不建议在生产环境中使用。

6.Mapper 接口的工作原理是什么?Mapper 接口里的方法,参数不同时,方法能重载吗?

Mapper接口是Mybatis提供的一种编写SQL映射的方式,可以将SQL语句与Java方法进行关联,使得Java开发人员可以使用面向对象的方式来编写数据访问代码。Mapper接口的工作原理是:

  1. 定义Mapper接口:首先,开发人员需要定义一个Java接口,其中每个方法代表一条SQL语句,方法名与SQL语句的ID一致,方法的参数和返回值类型与SQL语句的参数和返回值类型一致。
  2. 映射Mapper接口:然后,将Mapper接口与SQL语句进行映射,可以使用XML文件或注解的方式进行映射。映射的方式包括指定SQL语句的ID、参数类型、返回值类型等信息。
  3. 使用Mapper接口:最后,在Java代码中使用Mapper接口来进行数据访问,通过Mybatis框架将Mapper接口中的方法与SQL语句进行绑定,实现对数据库的访问操作。

至于Mapper接口中的方法重载问题,答案是:不可以。在Mapper接口中,每个方法名对应着一个唯一的SQL语句ID,方法名相同会造成冲突,因此Mapper接口中的方法不允许重载。如果需要执行不同的SQL语句,可以使用不同的方法名来实现。

7.MyBatis 是如何进行分页的?分页插件的原理是什么?

1.MyBatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页;

2.MyBatis可以使用SQL语句中的LIMIT关键字实现简单的分页,但是对于大数据量的分页查询,需要使用更高效的方法。

3.MyBatis提供了一种基于拦截器的分页插件来优化分页查询。

分页插件的原理是在MyBatis执行SQL语句之前,对其进行拦截并修改SQL语句,从而实现分页查询的功能。具体步骤如下:

  1. 拦截器初始化:插件在MyBatis启动时进行初始化,创建拦截器对象并将其添加到MyBatis的拦截器链中。
  2. 拦截器拦截:当进行分页查询时,拦截器会拦截对应的SQL语句,并在其中添加分页的限制条件,例如使用LIMIT关键字进行分页。
  3. 执行SQL语句:修改后的SQL语句会被传递给MyBatis的执行器,执行器会将SQL语句发送到数据库进行查询,并返回查询结果。
  4. 封装查询结果:查询结果会被封装到MyBatis的结果对象中,并返回给调用方。

目前比较流行的MyBatis分页插件是PageHelper,其基于拦截器的原理,在拦截器中实现了分页查询的逻辑,并提供了丰富的配置选项和API,可以方便地实现各种复杂的分页查询需求。使用PageHelper进行分页查询的示例代码如下:

// 指定分页查询的页码和每页记录数
PageHelper.startPage(pageNum, pageSize);
// 执行查询操作,返回分页查询结果
List userList = userDao.selectUserList();
// 封装分页查询结果
PageInfo pageInfo = new PageInfo<>(userList);

在这段代码中,我们使用了PageHelper的startPage方法来指定分页查询的页码和每页记录数,然后执行普通的查询操作,PageHelper会自动拦截并修改SQL语句,实现分页查询的功能。最后,我们将查询结果封装到PageInfo对象中,以便进行分页信息的显示和处理。

8.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

MyBatis将SQL执行结果封装为目标对象并返回,主要通过以下两个步骤来完成:

  1. 映射SQL执行结果到结果集对象:在执行SQL查询之后,MyBatis将结果集中的每一行映射为一个结果集对象,并将所有结果集对象封装到一个List集合中。
  2. 映射结果集对象到目标对象:MyBatis根据配置文件中的映射关系,将结果集对象的属性值映射到目标对象的属性中,并返回映射后的目标对象。

MyBatis支持多种映射形式,常用的有以下三种:

  1. 基于XML的映射:通过XML配置文件来描述结果集对象和目标对象之间的映射关系。
  2. 基于注解的映射:通过Java注解来描述结果集对象和目标对象之间的映射关系。
  3. 基于API的映射:通过Java代码来描述结果集对象和目标对象之间的映射关系。

其中,基于XML的映射是最常用的一种方式。在XML配置文件中,我们可以使用标签来定义结果集对象和目标对象之间的映射关系,例如:



  
  
  




在这段代码中,我们首先定义了一个名为userResultMap标签,用于描述结果集对象和目标对象之间的映射关系。其中,type属性指定了目标对象的类型,标签用于描述每个属性之间的映射关系。然后,在执行SQL查询时,我们指定了resultMap属性为userResultMap,表示使用这个映射关系来将结果集映射为目标对象。

除了标签,MyBatis还提供了其他的映射标签,例如等,可以根据实际需要来灵活配置映射关系。

9.如何执行批量插入?能返回主键id吗

要执行批量插入操作,可以使用 MyBatis 提供的 batch 执行器和 foreach 标签。

首先,我们需要将需要插入的数据封装到一个 List 或数组中。然后,通过 foreach 标签将数据逐个插入到数据库中。

示例如下:


  
    INSERT INTO user(name, age) VALUES(#{user.name}, #{user.age})
  

在这个示例中,我们定义了一个名为 batchInsertUsers 的 SQL 语句,该语句接受一个 List 类型的参数。通过 foreach 标签,将 List 中的每个元素逐个插入到数据库中。

如果需要返回插入数据的主键 ID,可以使用 MyBatis 提供的 useGeneratedKeyskeyProperty 属性。

示例如下:


  
    INSERT INTO user(name, age) VALUES(#{user.name}, #{user.age})
  

在这个示例中,我们在 SQL 语句中添加了 useGeneratedKeys="true" 属性,表示使用数据库自动生成的主键 ID。然后,通过 keyProperty 属性将主键 ID 映射到 Java 对象的属性中。执行完成后,MyBatis 会将插入数据的主键 ID 封装到 Java 对象中返回。注意,在使用自动生成主键 ID 时,需要确保数据库表的主键字段设置为自增长或序列类型。

10.MyBatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?

MyBatis 动态 SQL 是为了解决 SQL 语句灵活性不足的问题而提出的一种技术。动态 SQL 可以根据条件拼接 SQL 语句,从而满足不同的查询需求。MyBatis 提供了以下几种动态 SQL 标签:

  1. :条件判断标签,当条件成立时才执行其中的 SQL 语句。
  2. :选择判断标签,根据条件选择不同的 SQL 语句执行。
  3. :SQL 语句修饰标签,用于在 SQL 语句的前后加上修饰字符,如 WHERE、AND、OR 等。
  4. :遍历标签,用于遍历一个集合并将集合中的元素添加到 SQL 语句中。

动态 SQL 的执行原理是,当 MyBatis 执行动态 SQL 语句时,会将 SQL 语句和参数传递给 SQL 解析器进行解析。SQL 解析器会根据 SQL 语句中的动态标签和参数的值,生成一个完整的 SQL 语句。然后,MyBatis 将生成的 SQL 语句和参数传递给 JDBC 驱动程序进行执行。最终,JDBC 驱动程序将执行结果返回给 MyBatis。

使用动态 SQL 可以使 SQL 语句更灵活、更具可读性和可维护性,也可以提高应用程序的性能和效率。但是,需要注意使用动态 SQL 时要避免 SQL 注入攻击。建议使用 MyBatis 提供的参数绑定功能,将参数值与 SQL 语句分离,从而避免 SQL 注入攻击。

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

是的,MyBatis 可以执行一对一、一对多的关联查询。

在 MyBatis 中,实现一对一、一对多关联查询的方式有两种:

  1. 嵌套查询(Nested Query):通过在 ResultMap 中定义关联对象的 ResultMap,然后在 SQL 语句中使用嵌套查询(通常使用子查询)来完成关联查询。这种方式适用于数据量较小的场景,但如果数据量很大,可能会影响查询性能。
  2. 嵌套结果(Nested Results):通过在 ResultMap 中使用 Association 和 Collection 标签,定义关联对象的 ResultMap,然后通过查询结果集的方式来完成关联查询。这种方式适用于数据量较大的场景,因为它可以在一次 SQL 查询中完成关联查询,从而提高查询性能。

两种方式的区别在于执行方式和性能。嵌套查询会执行多条 SQL 语句,每个 SQL 语句返回一个结果集,最终将结果集合并为一个对象;而嵌套结果只执行一条 SQL 语句,返回一个结果集,然后将结果集中的数据映射到对象中。

总的来说,如果数据量较小,建议使用嵌套查询;如果数据量较大,建议使用嵌套结果。但需要注意的是,使用嵌套结果时需要在 SQL 语句中使用 JOIN 操作,所以需要对 SQL 语句的性能进行优化,以避免出现慢查询的情况。同时,在定义 ResultMap 和 SQL 语句时需要注意标签的使用和语句的正确性,以确保关联查询的正确性和性能。

12.MyBatis 是否支持延迟加载?如果支持,它的实现原理是什么?

对的,MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,其中 association 指的是一对一,collection 指的是一对多查询。

在 MyBatis 配置文件中,可以通过设置 lazyLoadingEnabled 属性来启用或禁用延迟加载。当 lazyLoadingEnabled 设置为 true 时,MyBatis 将会开启延迟加载功能。此时,只有在实际使用对象时才会触发关联对象的查询操作,否则不会查询数据库。

MyBatis 延迟加载的原理是,在执行主查询语句时,仅仅查询主表的数据,而不去查询关联表的数据,将关联表的查询延迟到实际需要使用关联对象时再去查询。MyBatis 通过使用代理模式,在查询主表数据时创建代理对象,当实际需要使用关联对象时,再去查询关联表数据并设置到代理对象中,从而实现了延迟加载。

在具体实现时,MyBatis 通过 CGLIB 或 JDK 动态代理来创建代理对象,当代理对象的方法被调用时,会触发代理拦截器的方法。拦截器会检查关联对象是否已经加载,如果未加载,则发送延迟加载的 SQL 语句查询关联表数据,并将查询结果设置到代理对象中。这样,在实际使用关联对象时,就可以避免不必要的关联表查询,从而提高查询性能。

需要注意的是,MyBatis 的延迟加载功能需要配合 lazyLoadingEnabled 属性一起使用,并且需要遵循一些规范和约束,如不能在延迟加载时关闭 SqlSession,不能使用多线程并发访问等。同时,在使用延迟加载时,也需要注意潜在的 N+1 查询问题,需要合理设计 SQL 查询语句,避免不必要的性能损耗。

13.MyBatis 的 xml 映射文件中,不同的 xml 映射文件的id 是否可以重复?

不同的 xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置 namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。

原因就是 namespace+id 是作为 Map 的 key 使用的,如果没有 namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就可以重复,namespace 不同,namespace+id 自然也就不同。

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

MyBatis 有三种 Executor 执行器,分别是:

  1. SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。
  2. ReuseExecutor:执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map 内,供下一次使用。简言之,就是重复使用 Statement 对象。
  3. BatchExecutor:执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理的。

其中,SimpleExecutor 是每次请求都会创建一个 Statement 对象,非常耗费性能;ReuseExecutor 在多次请求中,如果有相同的 SQL 语句,就会复用 Statement 对象,优化了性能;BatchExecutor 是执行批量操作时使用,可以一次性执行多条 SQL 语句,优化性能。

在 MyBatis 中,默认使用的是 SimpleExecutor 执行器。可以通过在 MyBatis 的配置文件中指定 ,来改变默认的执行器。

15.MyBatis 中如何指定使用哪一种 Executor 执行器?

在 MyBatis 的配置文件中可以通过 defaultExecutorType 属性来指定默认的执行器类型

另外,针对每个 SELECT * FROM user WHERE id = #{id}

16.MyBatis 是否可以映射 Enum 枚举类?

是的,MyBatis可以映射Java中的Enum枚举类。MyBatis提供了两种方式来映射Enum枚举类:

1.使用EnumTypeHandler:EnumTypeHandler是MyBatis内置的类型处理器之一,它可以将Java中的Enum枚举类与数据库中的数据进行相互转换。

例如,在Mapper XML文件中,可以这样使用EnumTypeHandler来映射枚举类型:


  
  

在这个例子中,我们将数据库中的gender列与Java中的User类的gender属性进行映射,并使用EnumTypeHandler类型处理器进行转换。

2.使用注解@EnumValue:如果在枚举类中使用了@EnumValue注解,则MyBatis会自动将枚举值与数据库中的数据进行相互转换。

例如,在定义枚举类时,可以使用@EnumValue注解来标识该枚举值对应的数据库中的值:

public enum Gender {
  MALE("M"),
  FEMALE("F");

  private String value;

  Gender(String value) {
    this.value = value;
  }

  @EnumValue
  public String getValue() {
    return value;
  }
}

在这个例子中,我们将MALE枚举值对应的数据库值设为"M",将FEMALE枚举值对应的数据库值设为"F"。然后,在Mapper XML文件中,就可以直接使用枚举类型进行映射,例如:


  
  

在这个例子中,MyBatis会根据@EnumValue注解中定义的值,将数据库中的gender列与Java中的User类的gender属性进行相互转换。

17.简述 MyBatis 的 xml 映射文件和 MyBatis 内部数据结构之间的映射关系?

MyBatis 将 XML 映射文件中的配置信息映射成 Configuration 对象,该对象是 MyBatis 中重要的数据结构之一,包含了所有的配置信息,包括数据库连接信息、映射文件信息、缓存配置信息等等。Configuration 对象内部包含许多其他对象,包括:

  • MappedStatement:封装了一个 SQL 语句的信息,如 SQL 语句、输入参数、输出结果等。
  • SqlSource:封装了一个 SQL 语句的信息,如 SQL 语句、输入参数等,但不包含输出结果。
  • BoundSql:表示绑定了 SQL 语句和实际参数值的 SQL 语句,包含了 SQL 语句、参数值等信息。
  • ParameterMap:表示参数映射关系的对象,包含了参数的名称、类型等信息。
  • ParameterMapping:表示一个参数映射关系,包含了参数名称、类型等信息。
  • ResultMap:表示结果集映射关系的对象,包含了结果集字段与 Java 对象属性之间的映射关系。
  • ResultMapping:表示一个结果集映射关系,包含了结果集字段与 Java 对象属性之间的映射关系。

在解析 XML 映射文件时,MyBatis 会将