Bootstrap

MyBatis常见面试题总结

一、MyBatis基本概念

  1. 什么是MyBatis?
    • MyBatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
  2. MyBatis的优缺点是什么?
    • 优点
      • 简单易学:MyBatis的设计简单直观,容易上手。
      • 灵活性强:允许开发人员编写自定义SQL语句,能够根据项目需求进行灵活配置和扩展。
      • 可读性好:使用简洁的XML或注解配置方式,可以清晰地表达SQL语句和数据映射关系。
      • 性能高:提供了高效的缓存机制,能够有效地减少数据库操作的次数,并支持批量操作和分页查询等功能。
      • 易于集成:可以与多种数据库和Web框架无缝集成。
    • 缺点
      • 学习成本较高:需要开发人员熟悉SQL语句的编写和调优。
      • 配置较为繁琐:MyBatis的配置文件较多,需要开发人员仔细配置,否则容易出现错误。
      • SQL语句调试困难:MyBatis将SQL语句和Java代码分离,当SQL语句出现问题时,调试起来相对困难。
      • 不适合小型项目:对于小型项目而言,MyBatis的优势可能不够明显,反而会增加项目的开发成本和复杂度。

二、MyBatis核心组件与配置

  1. MyBatis有哪些核心组件?
    • SqlSessionFactory:创建和管理SqlSession对象的工厂。
    • SqlSession:执行SQL语句的接口,管理数据库会话。
    • Executor:负责执行SQL语句,管理缓存和事务。
    • Mapper接口:映射SQL语句与Java对象的方法接口。
    • Configuration:全局配置类,包含MyBatis的所有配置信息。
  2. MyBatis的编程步骤是什么样的?
    • 引入MyBatis依赖。
    • 配置MyBatis环境,编写mybatis-config.xml,配置数据库连接信息、别名、映射文件等。
    • 使用XML文件编写SQL映射(或用注解方式)。
    • 定义接口(Mapper),与SQL映射文件中的SQL语句进行绑定。
    • 创建SqlSessionFactory,通过SqlSessionFactoryBuilder读取配置文件,生成SqlSessionFactory。
    • 通过SqlSessionFactory获取SqlSession。
    • 调用Mapper接口执行增删改查操作。
    • 关闭SqlSession以释放资源。
  3. MyBatis的Xml映射文件中,有哪些标签?
    • CRUD操作标签:包括select、insert、update、delete等标签,用于定义对数据库的增、删、改、查操作。
    • 结果集映射标签:包括<resultMap>和<result>标签,用于定义Java对象和数据库表之间的映射关系。
    • SQL片段标签:包括<sql>和<include>标签,用于定义可重用的SQL代码片段。
    • 动态SQL标签:包括<if>、<choose>、<when>、<otherwise>、<where>、<set>、<foreach>和<bind>等标签,用于动态生成SQL语句。

三、MyBatis高级特性

  1. #{}和${}的区别是什么?
    • #{}:是sql的参数占位符,MyBatis会将sql中的#{}替换为问号占位符(?),然后使用PreparedStatement进行预编译,最后将实际的参数值设置到预编译语句中。使用#{}可以有效地防止SQL注入等安全问题,同时也可以避免一些数据类型转换的问题。
    • **∗∗:是字符串替换,直接将参数值替换到SQL语句中。这种方式的好处是可以直接拼接字符串,但也带来了一些安全问题,如SQL注入风险。因此,使用{}时需要开发人员自行保证参数的合法性。
  2. MyBatis是如何进行分页的?分页插件的原理是什么?
    • MyBatis可以使用RowBounds对象进行分页,这是针对ResultSet结果集执行的内存分页,而非物理分页。此外,也可以在SQL语句中直接书写带有物理分页的参数来完成物理分页功能,或者使用分页插件来完成物理分页。分页插件的基本原理是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的SQL,然后重写SQL,根据数据库方言添加对应的物理分页参数。
  3. MyBatis是如何将sql执行结果封装为目标对象并返回的?有哪些映射形式?
    • MyBatis通过两种方式将SQL执行结果封装为目标对象并返回:一是使用标签逐一定义数据库列名和对象属性名之间的映射关系;二是使用SQL列的别名功能,将列的别名书写为对象属性名。有了列名与属性名的映射关系后,MyBatis通过反射创建对象,并使用反射给对象的属性逐一赋值并返回。
  4. MyBatis动态SQL是做什么的?有哪些动态SQL?能简述一下动态SQL的执行原理吗?
    • MyBatis动态SQL允许在XML映射文件内以标签的形式编写动态SQL,完成逻辑判断和动态拼接SQL的功能。MyBatis提供了9种动态SQL标签,包括<if>、<choose>、<when>、<otherwise>、<where>、<set>、<foreach>和<bind>等。动态SQL的执行原理是使用OGNL从SQL参数对象中计算表达式的值,根据表达式的值动态拼接SQL,以此来完成动态SQL的功能。
  5. MyBatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别?
    • MyBatis不仅可以执行一对一、一对多的关联查询,还可以执行多对一、多对多的关联查询。关联对象查询有两种方式:一种是单独发送一个SQL去查询关联对象,赋给主对象,然后返回主对象;另一种是使用嵌套查询(即使用join查询),一部分列是A对象的属性值,另外一部分是关联对象B的属性值。好处是只发一个SQL查询,就可以把主对象和其关联对象查出来。

四、MyBatis缓存机制

  1. MyBatis的一级、二级缓存是什么?
    • 一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Session flush或close之后,该Session中的所有Cache就将清空。默认打开一级缓存。
    • 二级缓存:与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口。
  2. MyBatis中的缓存有哪些实现类型?
    • MyBatis中的缓存实现类型主要包括一级缓存(本地缓存)和二级缓存(全局缓存)。一级缓存是PerpetualCache的HashMap实现,存储作用域为Session;二级缓存也可以是PerpetualCache的HashMap实现,但存储作用域为Mapper(Namespace),并且可自定义存储源。

五、其他问题

  1. MyBatis是否支持延迟加载?如果支持,它的实现原理是什么?
    • MyBatis支持延迟加载。延迟加载的实现原理是在需要用到数据时才进行加载,而不是在初始化时就加载所有数据。这可以通过配置Mapper XML文件中的<association>和<collection>元素来实现。当访问这些元素时,MyBatis会动态地生成SQL语句并加载相关数据。
  2. MyBatis的xml映射文件中,不同的xml映射文件的id是否可以重复?
    • 在MyBatis中,不同的xml映射文件如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复。因为namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么id重复会导致数据互相覆盖。

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

在MyBatis中,#{}${}都是用来替换SQL语句中的参数的,但它们之间存在显著的区别。以下是对这两者的详细对比:

1. 功能与执行过程

  • #{}:是sql的参数占位符。MyBatis会将sql中的#{}替换为问号占位符(?),然后使用PreparedStatement进行预编译,最后将实际的参数值设置到预编译语句中。这种方式可以有效地防止SQL注入攻击,因为参数值在传递过程中会被自动转义。
  • ${}:是字符串替换。MyBatis会直接将参数值替换到SQL语句中,不进行预编译。这种方式的好处是可以直接拼接字符串,但也带来了一些安全问题,如SQL注入风险。因为参数值直接嵌入到SQL语句中,如果参数值包含恶意SQL代码,就可能导致SQL注入攻击。

2. 使用场景

  • #{}:通常用于传递普通参数,如数字、字符串等。由于它使用预编译的方式,所以能够有效地防止SQL注入攻击,并且可以提高数据库的性能。
  • ${}:通常用于传递SQL命令或SQL关键字,如列名、表名、排序字段等。但需要注意的是,在使用${}时需要开发人员自行保证参数的合法性,以避免SQL注入风险。此外,${}还可以用于动态拼接SQL语句,但同样需要谨慎使用。

3. 安全性

  • #{}:由于使用预编译的方式,所以能够有效地防止SQL注入攻击,是更安全的选择。
  • ${}:直接将参数值替换到SQL语句中,不进行预编译,存在SQL注入风险。因此,在使用时需要特别注意参数的合法性验证。

4. 示例

  • 使用#{}
    SELECT * FROM users WHERE id = #{userId}

    如果传入的userId为1,则解析后的SQL语句为:

    SELECT * FROM users WHERE id = ?

    此时,数据库会预编译这条SQL语句,并将?替换为实际的参数值1。

  • 使用${}
    SELECT * FROM users WHERE id = ${userId}

    如果传入的userId为1,则解析后的SQL语句为:

    SELECT * FROM users WHERE id = 1

    此时,参数值直接替换到了SQL语句中,没有进行预编译。如果传入的userId包含恶意SQL代码,就可能导致SQL注入攻击。

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

Dao 接口的工作原理

Dao(Data Access Object)接口是Java中用于访问数据库的一种设计模式。在MyBatis等持久层框架中,Dao接口通常与XML映射文件或注解一起使用,以定义和执行SQL语句。Dao接口的工作原理大致如下:

  1. 接口定义:开发者首先定义一个Java接口,接口中声明了用于数据库操作的方法,如增删改查等。

  2. SQL映射:然后,开发者需要在XML映射文件或使用注解的方式中,为Dao接口中的每个方法指定对应的SQL语句。这些SQL语句定义了如何与数据库进行交互。

  3. 框架代理:在运行时,MyBatis等框架会通过Java动态代理技术为Dao接口创建一个代理对象。当开发者调用Dao接口中的方法时,实际上是在调用这个代理对象。

  4. SQL执行:代理对象会根据方法的签名和XML映射文件或注解中的SQL语句,构建出完整的SQL命令,并通过JDBC与数据库进行交互。

  5. 结果映射:执行SQL命令后,代理对象还会将数据库返回的结果集映射为Java对象,并返回给开发者。

Dao 接口里的方法重载

在Java中,方法重载是指在一个类中可以有多个方法具有相同的名称,但它们的参数列表不同(参数个数或类型不同)。对于Dao接口来说,方法重载同样是适用的。

然而,在MyBatis中,方法重载有一些需要注意的地方:

  • XML映射:如果使用XML映射文件来定义SQL语句,那么每个<select><insert><update><delete>标签都需要一个唯一的id属性来标识。因此,对于Dao接口中重载的方法,你需要在XML映射文件中为每个方法指定不同的id。通常,可以通过方法的全限定名(包括包名、类名和方法名)或自定义的唯一标识符来实现这一点。

  • 注解映射:如果使用注解来定义SQL语句,那么MyBatis会根据方法的签名(包括方法名和参数列表)来找到对应的SQL语句。在这种情况下,方法重载是自然支持的,因为每个重载的方法都有唯一的签名。

  • 参数处理:对于重载的方法,如果它们的参数列表不同,MyBatis需要能够正确地识别并处理这些参数。这通常是通过Java的反射机制来实现的。因此,在定义重载方法时,需要确保参数的类型和顺序是清晰的,以避免混淆。

;