MyBatis面试专题

文章目录

  • 什么是 MyBatis?
  • 讲下 MyBatis 的缓存
      • 一级缓存
      • 二级缓存
  • Mybatis 是如何进行分页的?分页插件的原理是什么?
    • 分页插件的原理
    • 举例说明
  • 简述 Mybatis 的插件运行原理,以及如何编写一个插件?
    • 插件运行原理
    • 编写一个插件的基本步骤
  • Mybatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
    • 动态 SQL 的执行原理
  • #{}和${}的区别是什么?
  • 为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
    • MyBatis 的特点:
    • 全自动 ORM 映射工具的特点(如 Hibernate):
    • 区别总结:
  • Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
  • MyBatis 与 Hibernate 有哪些不同?
  • MyBatis 的好处是什么?
  • 简述 Mybatis 的 Xml 映射文件和 Mybatis 内部数据结构之间的映射关系?
  • 什么是 MyBatis 的接口绑定,有什么好处?
  • 接口绑定有几种实现方式,分别是怎么实现的?
  • 什么情况下用注解绑定,什么情况下用 xml 绑定?
  • MyBatis 实现一对一有几种方式?具体怎么操作的?
  • Mybatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别?
  • MyBatis 里面的动态 Sql 是怎么设定的?用什么语法?
  • Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
  • Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?
  • 当实体类中的属性名和表中的字段名不一样,如果将查询的结果封装到指定 pojo?
  • 模糊查询 like 语句该怎么写
  • 通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应, Dao 的工作原理,是否可以重载?
  • Mybatis 映射文件中,如果 A 标签通过 include 引用了 B 标签的内容,请问,B 标签能否定义在 A 标签的后面,还是说必须定义在 A 标签的前面?
  • Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?
  • Mybatis 中如何执行批处理?
  • Mybatis 都有哪些 Executor 执行器?它们之间的区别是什么?
  • Mybatis 中如何指定使用哪一种 Executor 执行器?
  • Mybatis 执行批量插入,能返回数据库主键列表吗?
  • Mybatis 是否可以映射 Enum 枚举类?
  • 如何获取自动生成的(主)键值?
  • 在 mapper 中如何传递多个参数?
  • resultType resultMap 的区别?
  • 使用 MyBatis 的 mapper 接口调用时有哪些要求?
  • Mybatis 比 IBatis 比较大的几个改进是什么?
  • IBatis 和 MyBatis 在核心处理类分别叫什么?
  • IBatis 和 MyBatis 在细节上的不同有哪些?


什么是 MyBatis?

MyBatis 是一个持久层框架,它主要用于简化数据库操作,并提供了灵活的 SQL 编写和结果映射功能。其主要特点包括:

  1. SQL 映射配置文件:MyBatis 使用 XML 文件或注解来配置 SQL 语句,从而将 SQL 语句与 Java 方法进行映射。

  2. 灵活的 SQL 编写:MyBatis 允许开发人员编写自定义的 SQL 语句,可以直接在 XML 文件中编写 SQL,也可以使用注解方式在 Java 代码中编写。

  3. 参数映射:MyBatis 支持将 Java 对象作为参数传递给 SQL 语句,并可以自动映射参数到 SQL 语句中。

  4. 结果映射:MyBatis 提供了灵活的结果映射功能,可以将 SQL 查询结果映射到 Java 对象中,支持一对一、一对多等复杂关系的映射。

  5. 事务支持:MyBatis 提供了对事务的支持,可以使用声明式或编程式事务管理来管理数据库操作。

  6. 缓存机制:MyBatis 提供了一级缓存和二级缓存的支持,可以提高数据库访问性能。

  7. 与 Spring 集成:MyBatis 可以与 Spring 框架无缝集成,可以通过 Spring 提供的事务管理和依赖注入功能来管理 MyBatis 的 SQL 会话和映射器。

总的来说,MyBatis 是一个功能强大且灵活的持久层框架,它提供了丰富的功能和灵活的配置选项,可以帮助开发人员简化数据库操作,并提高系统的性能和可维护性。

讲下 MyBatis 的缓存

MyBatis 提供了一级缓存和二级缓存来提高数据库查询性能和减少数据库交互次数。下面分别介绍一下这两种缓存的特点和使用方法:

一级缓存

一级缓存是指在同一个 SQLSession 内部的缓存。默认情况下,每个 SQLSession 对象都拥有自己的一级缓存,且一级缓存是开启的。一级缓存的特点如下:

  • 生命周期:一级缓存的生命周期与 SQLSession 相同,即在同一个 SQLSession 中执行的多次查询可以共享一级缓存。
  • 作用范围:一级缓存仅限于同一个 SQLSession 中,不同的 SQLSession 之间的缓存是相互独立的。
  • 自动刷新:一级缓存是自动刷新的,当执行了增删改操作时,会自动清空对应的一级缓存。

二级缓存

二级缓存是指多个 SQLSession 共享的缓存,可以跨越多个 SQLSession,多个 Mapper 接口之间可以共享二级缓存。但是需要注意,二级缓存是需要手动开启的,且默认情况下是关闭的。二级缓存的特点如下:

  • 生命周期:二级缓存的生命周期与整个应用程序相同,即多个 SQLSession 之间可以共享二级缓存。
  • 作用范围:二级缓存是全局的,多个 SQLSession 之间可以共享二级缓存。
  • 配置:需要在映射文件中进行配置,使用 标签来启用和配置二级缓存。

<mapper namespace="com.example.mapper.UserMapper">
    <cache/>
mapper>
  • 序列化要求:为了支持跨 SQLSession 序列化和反序列化对象,缓存的对象需要实现 Serializable 接口。

使用缓存时需要注意以下几点:

  • 对于频繁更新的数据,不建议使用缓存,因为会导致缓存频繁失效,反而增加了数据库的负担。
  • 对于不经常更新的数据,可以适当使用缓存来提高查询性能,但需要注意缓存的命中率和缓存的更新策略。
  • 在需要的时候可以手动清除缓存,可以通过调用 sqlSession.clearCache() 方法来清空一级缓存,或者通过 来引用其他命名空间的缓存。

综上所述,MyBatis 的缓存功能可以有效地提高数据库查询性能,但需要根据实际情况进行合理配置和使用。

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

MyBatis 实现分页的方式有多种,包括使用 RowBounds 对象、直接编写 SQL 实现分页以及使用 MyBatis 的分页插件。下面我将详细介绍 MyBatis 分页插件的原理和实现方式:

分页插件的原理

MyBatis 分页插件的原理是通过自定义拦截器(Interceptor)来拦截待执行的 SQL,并在拦截器中重写 SQL 实现分页功能。具体步骤如下:

  1. 实现自定义插件(Interceptor):首先,需要编写一个实现了 MyBatis 的 Interceptor 接口的自定义插件,该插件用于拦截 SQL 执行过程中的方法。

  2. 拦截待执行的 SQL:在自定义插件中,通过重写 MyBatis 的 Executor 对象的 query 方法,拦截待执行的 SQL 语句。

  3. 解析原始 SQL:在拦截到待执行的 SQL 后,需要对原始 SQL 进行解析,获取其中的表名、字段等信息,以便后续的分页处理。

  4. 重写 SQL:在解析完原始 SQL 后,根据数据库类型(如 MySQL、Oracle 等)以及分页参数(如页码、每页数量)等信息,构造相应的分页 SQL。

  5. 执行重写后的 SQL:最后,使用重写后的 SQL 来执行数据库查询,并返回查询结果,实现分页功能。

举例说明

假设原始 SQL 为 SELECT * FROM student,我们希望实现查询结果的分页显示,每页显示 10 条数据,第一页从第 0 条开始。下面是一个简单的分页插件的示例:

public class PaginationInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截待执行的 SQL
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        
        // 解析原始 SQL
        BoundSql boundSql = ms.getBoundSql(parameter);
        String originalSql = boundSql.getSql();
        
        // 构造分页 SQL
        String pageSql = buildPageSql(originalSql, rowBounds);
        
        // 重写 SQL
        MetaObject metaStatementHandler = SystemMetaObject.forObject(invocation.getTarget());
        metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
        
        // 执行重写后的 SQL
        return invocation.proceed();
    }

    private String buildPageSql(String originalSql, RowBounds rowBounds) {
        // 构造分页 SQL,这里以 MySQL 为例
        StringBuilder pageSql = new StringBuilder(originalSql);
        pageSql.append(" LIMIT ")
               .append(rowBounds.getOffset())
               .append(",")
               .append(rowBounds.getLimit());
        return pageSql.toString();
    }
}

在上面的示例中,我们实现了一个简单的分页插件 PaginationInterceptor,通过重写拦截器的 intercept 方法,在其中拦截待执行的 SQL,并根据 RowBounds 对象中的分页信息构造分页 SQL,最后通过反射将重写后的 SQL 设置到原始的 BoundSql 中。这样就实现了 MyBatis 的分页功能。

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

你提到的是 MyBatis 的插件功能,下面我将简要介绍 MyBatis 插件的运行原理以及编写一个插件的基本步骤:

插件运行原理

MyBatis 插件的运行原理基于动态代理。当 MyBatis 执行需要拦截的接口方法时,会通过动态代理生成一个代理对象,并在代理对象的方法中调用插件的拦截方法。具体步骤如下:

  1. MyBatis 会为需要拦截的接口生成代理对象。
  2. 当调用代理对象的方法时,实际上会调用插件的拦截方法。
  3. 在插件的拦截方法中,可以对方法的参数和返回值进行处理,实现自定义的功能。
  4. 最终,插件可以选择继续执行原始方法,也可以选择终止执行。

编写一个插件的基本步骤

下面是编写一个 MyBatis 插件的基本步骤:

  1. 创建一个类,实现 MyBatis 的 Interceptor 接口。
  2. 在实现的 Interceptor 接口中实现 intercept() 方法,这是插件的核心方法,用于拦截需要处理的接口方法。
  3. 在 intercept() 方法中编写插件的具体逻辑,可以对方法的参数和返回值进行处理,实现自定义的功能。
  4. 使用 @Intercepts 注解指定需要拦截的接口方法,可以指定多个接口方法。
  5. 将插件注册到 MyBatis 的配置文件中,使其生效。

下面是一个简单的示例:

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MyPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截 Executor 的 update 方法
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        
        // 在此处编写插件的具体逻辑
        
        // 调用原始方法
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        // 生成代理对象
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置插件属性
    }
}

在上面的示例中,我们创建了一个名为 MyPlugin 的插件,通过 @Intercepts 注解指定需要拦截的方法为 Executor 接口的 update 方法。在 intercept() 方法中,我们可以编写插件的具体逻辑,对方法的参数和返回值进行处理。最后,通过 Plugin.wrap() 方法生成代理对象,使插件生效。

在 MyBatis 的配置文件中,需要将插件注册为插件列表的一部分,以便插件生效。例如:

<plugins>
    <plugin interceptor="com.example.MyPlugin">
        
    plugin>
plugins>

以上就是编写一个 MyBatis 插件的基本步骤。

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

动态 SQL 的执行原理

  1. 解析和组装: MyBatis 在解析 XML 映射文件时,会识别并解析动态 SQL 标签,例如 , , 等。
  2. 条件判断: 对于带有条件判断的标签,如 ,MyBatis 会通过 OGNL 表达式计算条件的值,根据条件的结果来决定是否执行标签内的 SQL 语句。
  3. 循环处理: 对于循环标签,如 ,MyBatis 会遍历集合并根据集合的元素动态生成对应的 SQL 片段。
  4. SQL 组装: MyBatis 将经过条件判断和循环处理后的 SQL 片段进行组装,形成完整的 SQL 语句。
  5. 参数绑定: MyBatis 将参数绑定到 SQL 语句中,确保参数的值正确传递到 SQL 语句中的占位符处。
  6. SQL 执行: 最后,MyBatis 将完整的 SQL 语句发送给数据库执行,从而完成动态 SQL 的执行过程。

总的来说,MyBatis 的动态 SQL 执行原理就是根据 XML 映射文件中的动态 SQL 标签,结合参数对象的属性值,动态生成 SQL 语句,并将参数值绑定到 SQL 中,最终执行生成的 SQL 语句。

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

  1. 预编译处理 vs. 字符串替换:

    • #{} 是 MyBatis 的参数占位符,它会被解析为一个问号 ?,并由 JDBC 预编译处理。这意味着参数值会作为参数传递给 SQL 语句,可以有效防止 SQL 注入攻击。
    • ${} 则是简单的字符串替换,它会被直接替换为参数的值,不会被预编译处理。这样的话,如果不谨慎处理用户输入,可能会导致 SQL 注入攻击。
  2. 安全性:

    • 由于 #{} 是预编译处理,因此具有更高的安全性,能够有效防止 SQL 注入攻击。
    • ${} 则较为脆弱,如果直接将用户输入的值放在 ${} 中,可能会导致 SQL 注入问题。
  3. 参数传递方式:

    • #{} 是通过参数绑定的方式,MyBatis 会将参数值以安全的方式传递给 SQL 语句,适用于大多数场景。
    • ${} 则是简单的字符串替换,适用于一些特殊情况,如动态拼接 SQL。
  4. 语法结构:

    • #{} 在 SQL 中使用,表示参数占位符,例如 WHERE id = #{userId}
    • ${} 则是字符串替换,将其内部的内容替换为实际的值,例如 WHERE id = ${userId}

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

MyBatis 的特点:

  1. 半自动化: MyBatis 是一种半自动的 ORM(对象关系映射)工具,它需要手动编写 SQL 查询语句来完成对象与关系数据库之间的映射。这意味着开发人员需要显式地定义 SQL 查询,并且需要处理结果集的映射。

  2. 灵活性: MyBatis 提供了灵活性,开发人员可以直接编写 SQL,从而更好地控制 SQL 的执行过程。这使得开发人员可以根据实际需求进行优化和调整,以获得更好的性能和更精确的结果。

  3. SQL 控制权: 开发人员可以直接控制 SQL 的编写和执行过程,包括优化、调整和定制 SQL 查询。这样可以更好地理解和掌握底层数据库操作,适用于一些复杂的业务场景。

全自动 ORM 映射工具的特点(如 Hibernate):

  1. 全自动化: 全自动 ORM 工具(如 Hibernate)通过对象关系映射来实现,开发人员无需编写 SQL 查询语句,而是通过对象之间的关系来查询和操作数据库。这使得开发过程更加简单和高效。

  2. 透明性: 全自动 ORM 工具隐藏了底层数据库操作细节,开发人员不需要关心数据库的具体实现细节,只需关注业务对象和关系之间的映射关系。

  3. 学习曲线低: 由于全自动 ORM 工具抽象了底层数据库操作,因此开发人员可以更快地上手,并且无需深入了解 SQL 查询语句的编写和优化。

区别总结:

  • MyBatis 是一种半自动 ORM 映射工具,需要开发人员手动编写 SQL 查询语句,具有灵活性和可控性。
  • 全自动 ORM 映射工具(如 Hibernate)则隐藏了底层数据库操作细节,提供了更简单、更便捷的开发方式,但可能会牺牲一些灵活性和可控性。

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

  1. 支持延迟加载

    • Mybatis仅支持关联对象(association)和关联集合对象(collection)的延迟加载。关联对象指的是一对一关系,关联集合指的是一对多查询。
    • 在Mybatis配置文件中,可以设置是否启用延迟加载,即lazyLoadingEnabled=true|false
  2. 实现原理

    • Mybatis通过使用CGLIB创建目标对象的代理对象来实现延迟加载。
    • 当调用目标方法时,进入拦截器方法。例如,调用a.getB().getName(),拦截器的invoke()方法检测到a.getB()为null值,就会单独发送预先保存的查询关联B对象的SQL。
    • 查询到B对象后,调用a.setB(b)来为对象a的属性b赋值。
    • 然后完成a.getB().getName()方法的调用。

MyBatis 与 Hibernate 有哪些不同?

  1. ORM框架差异

    • MyBatis并非完全的ORM框架,需要程序员自行编写SQL语句,但可以通过XML或注解方式配置SQL语句和Java对象的映射。
    • Hibernate是典型的ORM框架,负责对象/关系映射,程序员无需编写SQL语句,通过对象来操作数据库。
  2. 学习门槛和灵活性

    • MyBatis学习门槛低,简单易学,灵活度高,适用于对关系数据模型要求不高的软件开发,如互联网软件、企业运营类软件。
    • Hibernate学习门槛高,需要精通,但能提高开发效率,对于关系模型要求高的定制化软件开发更为适用。
  3. 数据库无关性

    • MyBatis缺乏数据库无关性,需要自定义多套SQL映射文件来支持多种数据库。
    • Hibernate具有良好的数据库无关性,能够更好地适应不同数据库环境。
  4. 性能和灵活性权衡

    • MyBatis可以严格控制SQL执行性能,但无法做到数据库无关性,需要权衡性能和灵活性。
    • Hibernate对于关系模型要求高的软件能够提高开发效率,但在性能和对象模型之间的权衡上需要有经验和能力。

总之,根据软件需求在有限资源环境下选择适合的框架最为重要,维护性、扩展性良好的软件架构才是最优选择。

MyBatis 的好处是什么?

  1. SQL与Java代码分离

    • MyBatis将SQL语句与Java代码分离,单独存放在XML文件中,提高了程序的可维护性和可读性。
  2. 封装底层JDBC细节

    • MyBatis封装了底层JDBC API的调用细节,使得数据库操作更加简单,程序员无需处理繁琐的JDBC操作,同时能够自动将结果集转换成Java Bean对象。
  3. 灵活控制SQL语句

    • MyBatis需要程序员自行编写SQL语句,这使得程序员可以根据数据库特点灵活控制SQL语句的编写,从而实现更高效的查询和更复杂的操作。
    • 相比于全自动的ORM框架如Hibernate,MyBatis更容易实现高效的查询,能够满足复杂查询的需求。

总的来说,MyBatis通过SQL与Java代码分离、封装底层JDBC细节以及灵活控制SQL语句等特点,提供了更加灵活、可维护和高效的数据库操作方式。

简述 Mybatis 的 Xml 映射文件和 Mybatis 内部数据结构之间的映射关系?

在MyBatis中,XML映射文件中的各种标签会被解析成MyBatis内部的数据结构,主要映射关系如下:

  1. 标签:

    • 被解析为ParameterMap对象。
    • 的子元素会被解析为ParameterMapping对象。
  2. 标签:

    • 被解析为ResultMap对象。
    • 的子元素会被解析为ResultMapping对象。