Mybatis:结构/优缺点/SQL结果如何封装/动态SQL/#和$/dao层原理

最近准备面试,整理了一些关于 MyBatis 的常见问题,结合技术细节进行了深入解答,分享出来供大家参考。以下是问题的详细解析。

1. 什么是 MyBatis?MyBatis 的结构是怎样的?

MyBatis 是什么?

MyBatis 是一个基于 Java 的半自动化 ORM 框架,通过 XML 或注解配置 SQL,支持自定义查询、存储过程和结果映射。它简化了数据库操作,消除了传统 JDBC 中繁琐的资源管理和结果处理代码。

MyBatis 的结构:

  • 全局配置文件mybatis-config.xml):配置数据源、事务管理器、插件等。
  • Mapper 文件mapper.xml):定义 SQL 语句和结果映射规则。
  • SqlSessionFactory:通过配置文件构建的工厂类,单例模式,用于创建 SqlSession。
  • SqlSession:线程不安全的会话对象,提供执行 SQL 和事务管理的接口。
  • Mapper 接口:通过动态代理绑定 XML 中的 SQL,开发者只需调用方法即可。
  • Executor:底层执行器,有 SimpleExecutor、ReuseExecutor 等类型,负责 SQL 执行。
  • MappedStatement:封装了 SQL 语句、参数映射和结果映射的元数据。

2. 简述 MyBatis 的优缺点

优点:

  • 支持自定义 SQL,灵活性高,适合复杂查询。
  • 通过 XML 或注解配置,减少重复代码。
  • 与 Spring 集成良好,支持动态 SQL。

缺点:

  • SQL 需手动编写,工作量比全自动 ORM 大。
  • 动态 SQL 和配置有一定学习成本。
  • 大型项目中 SQL 文件管理可能复杂。

3. MyBatis 是怎么将 SQL 执行的结果封装为目标对象并返回的?

详细流程:

MyBatis 将 SQL 执行结果封装为目标对象的过程涉及多个组件和 Java 技术,以下是具体步骤:

  1. SQL 执行
    • SqlSession 调用 Executor(如 SimpleExecutor)执行 SQL。
    • Executor 通过 JDBC 的 PreparedStatement 执行查询,返回 ResultSet
  2. 元数据解析
    • MyBatis 根据 mapper.xml 中的 <resultMap> 或接口注解,解析列名与对象属性的映射关系。
    • 使用 Configuration 对象存储全局配置和映射信息。
  3. 结果处理
    • DefaultResultSetHandler(ResultSetHandler 的默认实现)接管结果集处理。
    • 通过 ResultSetWrapper 提取 ResultSet 的元数据(如列名、类型)。
  4. 对象实例化
    • 使用 Java 反射(ReflectorObjectFactory)创建目标对象实例(如 newInstance())。
  5. 属性填充
    • 根据 <resultMap><result> 标签或默认映射规则,将 ResultSet 中的值通过 TypeHandler 转换为 Java 类型。
    • 使用反射(Field.set() 或 Setter 方法)填充到对象属性中。
  6. 结果返回
    • 单条记录返回单个对象,多条记录通过 DefaultResultHandler 封装为 List 或其他集合。

用到的 Java 技术与对象:

  • 反射:创建对象、设置属性值。
  • JDBC:执行 SQL、获取 ResultSet。
  • TypeHandler :处理数据库类型与 Java 类型转换(如 IntegerTypeHandler)。
  • ResultSetHandler:核心组件,负责映射和封装。

MyBatis 干了什么活?

MyBatis 屏蔽了 JDBC 的底层细节(如连接管理、参数设置、结果遍历),通过反射和映射机制实现了自动化对象封装。

4. 动态 SQL 的作用,具体编写步骤,有哪些动态 SQL?

作用:

动态 SQL 允许根据输入参数动态生成 SQL,增强代码复用性,适用于条件查询、批量操作等场景。

具体编写步骤:

  1. 定义基础 SQL
    mapper.xml 中编写静态 SQL 框架,如 SELECT * FROM user
  2. 添加动态条件
    • 使用 <if> 标签根据参数拼接条件:

      xml 复制代码
      <if test="username != null and username != ''">
          AND username = #{username}
      </if>
      • AND/OR 的处理<where> 标签会自动移除多余的 AND/OR,例如:

        xml 复制代码
        <where>
            <if test="username != null">AND username = #{username}</if>
            <if test="age != null">AND age = #{age}</if>
        </where>

        输出:WHERE username = ? AND age = ?(无多余 AND)。

    • 使用 <set> 处理更新语句中的逗号:

      xml 复制代码
      <set>
          <if test="username != null">username = #{username},</if>
          <if test="age != null">age = #{age},</if>
      </set>

      输出:SET username = ?, age = ?(自动移除末尾逗号)。

  3. 循环处理
    • 使用 <foreach> 遍历集合,如 IN 查询:

      xml 复制代码
      <foreach collection="ids" item="id" open="(" close=")" separator=",">
          #{id}
      </foreach>

      输出:(1, 2, 3)

  4. 条件选择
    • 使用 <choose> 实现类似 switch-case:

      xml 复制代码
      <choose>
          <when test="type == 'A'">AND type = 'A'</when>
          <otherwise>AND type = 'B'</otherwise>
      </choose>

常见动态 SQL 标签:

  • <if>:条件拼接。
  • <where>:自动清理多余 AND/OR。
  • <set>:自动清理多余逗号。
  • <foreach>:处理集合循环。
  • <choose><when><otherwise>:条件分支。

注解支持:

通过 @Select 等注解结合 OGNL 表达式实现动态 SQL,但功能有限,通常 XML 更常用。

5. #{} 和 ${} 的区别

  • #{}
    • 预编译占位符,生成 PreparedStatement,如 WHERE id = ?
    • 防止 SQL 注入,参数值在执行时绑定。
    • 适用于普通参数替换。
  • ${}
    • 字符串拼接,直接嵌入 SQL,如 ORDER BY ${column}
    • 有 SQL 注入风险,仅用于动态表名或列名。
    • 不经过预编译处理。

建议 :优先用 #{},谨慎用 ${} 并做好参数校验。

6. 通常一个 mapper.xml 对应一个 DAO 接口,这个 DAO 接口的工作原理是什么?

详细内部过程:

MyBatis 的 DAO 接口无需手动实现,依赖动态代理自动生成实现类。以下是具体工作原理:

  1. 接口定义与绑定

    • 开发者定义 DAO 接口,如:

      java 复制代码
      public interface UserMapper {
          User getUserById(int id);
      }
    • mapper.xml 中,namespace 绑定接口全限定名,<select>id 与方法名对应:

      xml 复制代码
      <mapper namespace="com.example.UserMapper">
          <select id="getUserById" resultType="User">
              SELECT * FROM user WHERE id = #{id}
          </select>
      </mapper>
  2. 代理对象创建

    • 调用 SqlSession.getMapper(UserMapper.class) 时,MyBatis 使用 JDK 动态代理(Proxy.newProxyInstance)生成接口实现类。
    • 代理对象由 MapperProxy 类管理,内部持有 SqlSession 和接口的元数据。
  3. 方法调用拦截

    • 调用接口方法(如 getUserById(1))时,触发 MapperProxy.invoke()
    • MyBatis 根据方法名和 namespaceConfiguration 中定位对应的 MappedStatement
  4. SQL 执行

    • MappedStatement 包含 SQL 和映射规则,交给 SqlSessionselectOne()selectList() 执行。
    • Executor 调用 JDBC 执行 SQL,返回结果。
  5. 结果映射与返回

    • ResultSetHandler 将结果映射为目标对象(如 User),返回给调用方。

用到的技术与对象:

  • JDK 动态代理:生成代理对象。
  • 反射:解析接口方法与 XML 映射。
  • Configuration:存储全局配置和映射元数据。
  • MapperProxy:代理实现的核心类。

MyBatis 干了什么活?

MyBatis 通过动态代理屏蔽了 DAO 实现细节,开发者只需定义接口和 XML,就能完成数据库操作。

相关推荐
江沉晚呤时2 分钟前
深入解析外观模式(Facade Pattern)及其应用 C#
java·数据库·windows·后端·microsoft·c#·.netcore
uhakadotcom2 分钟前
云原生数据仓库对比:Snowflake、Databricks与阿里云MaxCompute
后端·面试·github
Asthenia041211 分钟前
常用索引有哪些?联合索引使用时要注意什么?什么是最左匹配原则?联合索引(a, b, c),使用(b, c) 可以命中索引吗?(a, c) 呢?
后端
Asthenia041229 分钟前
Redis性能与优势/对比其他Key-Value存储/数据类型及底层结构/相同数据结构原因/对比Memcached优势/字符串最大容量/RDB与AOF分析
后端
计算机-秋大田1 小时前
基于Spring Boot的个性化商铺系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
熬了夜的程序员1 小时前
Go 语言封装邮件发送功能
开发语言·后端·golang·log4j
uhakadotcom1 小时前
PostgreSQL 行级安全性(RLS)简介
后端·面试·github
小马爱打代码2 小时前
Spring Boot - 动态编译 Java 类并实现热加载
spring boot·后端
网络风云2 小时前
Flask(二)项目结构与环境配置
后端·python·flask
小杨4042 小时前
架构系列二十三(全面理解IO)
java·后端·架构