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,就能完成数据库操作。

相关推荐
WZTTMoon14 分钟前
开发中反复查的 Spring Boot 注解,一次性整理到位
java·spring boot·后端
长沙古天乐16 分钟前
Spring Boot应用中配置消费端随服务启动循环消费消息
spring boot·后端·linq
wadesir25 分钟前
掌握 Rust 中的浮点数处理(Rust f64 浮点数与标准库详解)
开发语言·后端·rust
IT_陈寒27 分钟前
React 18新特性实战:这5个Hook组合让我少写50%状态管理代码
前端·人工智能·后端
HashTang28 分钟前
【AI 编程实战】第 1 篇:TRAE SOLO 模式 10 倍速开发商业级全栈小程序
前端·后端·ai编程
bigdata-rookie1 小时前
Scala 泛型
开发语言·后端·scala
开心猴爷1 小时前
HTTPS Everywhere 时代的抓包挑战,从加密流量解析到底层数据流捕获的全流程方案
后端
卓码软件测评1 小时前
【第三方CNAS软件测试机构:Gatling中的资源监控_实时收集服务器CPU、内存、磁盘I/O和网络指标】
后端·测试工具·测试用例·scala·压力测试
DengRan1 小时前
别再手写 Java 二进制解析了 — 用 FastProto 从繁琐到优雅
后端
不是笨小孩1351 小时前
多元算力融合实践:openEuler在中等配置硬件环境下的性能验证
后端