MyBatis SQL结果封装原理及映射形式详解

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

一、MyBatis结果封装核心原理

MyBatis本质是通过JDBC结果集处理+Java反射+类型转换完成SQL结果到Java对象的映射,核心流程分为5步,全程无侵入式封装,兼顾灵活性与易用性:

  1. 执行SQL获取结果集:MyBatis通过Executor执行JDBC操作,调用Statement获取ResultSet结果集,这是封装的数据源。
  2. 解析映射规则:根据Mapper接口的返回值类型、XML/注解中的映射配置(resultType/resultMap),确定目标对象结构与字段对应关系。
  3. 遍历ResultSet行数据:逐行读取结果集数据,每一行对应一个目标对象实例,多行数据则组装为List/Set等集合。
  4. 字段映射与类型转换:通过TypeHandler完成数据库类型(如VARCHAR/INT/DATETIME)到Java类型(String/Integer/LocalDateTime)的转换,再通过反射赋值给对象属性。
  5. 组装返回对象:单行数据直接返回目标对象,多行数据封装为集合返回;关联查询则递归完成嵌套对象/集合的组装。

核心亮点:MyBatis通过结果集处理器(ResultSetHandler)统一管控封装逻辑,默认实现为DefaultResultSetHandler,开发者无需手动处理ResultSet遍历和反射赋值,大幅简化JDBC冗余代码。

二、MyBatis结果映射形式(按复杂度分类)

MyBatis映射形式分为基础简易映射自定义精准映射高级关联映射三大类,适配单表、多表、嵌套对象等不同业务场景,以下是详细拆解:

(一)基础简易映射(零配置/轻配置)

适用于单表查询、字段名匹配度高的场景,无需手动编写复杂映射规则,MyBatis自动完成封装。

1. 自动映射(resultType+驼峰命名)

这是最常用的基础映射,通过resultType指定目标Java对象,MyBatis根据字段名自动匹配完成赋值:

  • 默认匹配规则:数据库列名 = Java属性名(区分大小写);开启驼峰命名转换后,数据库下划线命名(user_name)自动映射Java驼峰属性(userName)。
  • 开启方式:全局配置文件中设置<setting name="mapUnderscoreToCamelCase" value="true"/>。
  • 适用场景:单表查询、字段名规范、无嵌套对象。
<!-- Mapper XML示例 -->
<select id="getUserById" resultType="com.demo.entity.User">
    select id, user_name, age from t_user where id = #{id}
</select>

2. 简单数据类型映射

查询结果为单值/单列时,直接映射为Java基本类型/包装类,无需自定义实体类:

  • 支持类型:Integer、Long、String、Date、LocalDateTime等。
  • 适用场景:统计查询(count/sum)、单字段查询。
<select id="getUserCount" resultType="java.lang.Integer">
    select count(1) from t_user
</select>

3. Map集合映射

无固定实体类时,将结果行封装为HashMap,数据库列名为key,列值为value,灵活适配动态字段场景:

  • 单行结果:返回Map<String, Object>;多行结果:返回List<Map<String, Object>>。
  • 适用场景:动态查询、临时数据封装、无固定实体类的报表查询。
<select id="getUserMapById" resultType="java.util.Map">
    select id, user_name, age from t_user where id = #{id}
</select>

(二)自定义精准映射(resultMap手动配置)

当数据库列名与Java属性名不匹配、需要指定类型转换、构造器赋值时,通过resultMap自定义映射规则,实现精准绑定。

1. 基础字段手动映射

通过<id>(主键)、<result>(普通字段)标签,手动指定列名与属性名的对应关系,解决命名不匹配问题。

<!-- 定义resultMap -->
<resultMap id="UserResultMap" type="com.demo.entity.User">
    <id column="id" property="id"/>
    <result column="user_name" property="userName"/>
    <result column="user_age" property="age"/>
    <result column="create_time" property="createTime" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler"/>
</resultMap>

<!-- 引用resultMap -->
<select id="getUserById" resultMap="UserResultMap">
    select id, user_name, user_age, create_time from t_user where id = #{id}
</select>

2. 构造器映射

针对无setter方法的实体类(如不可变对象),通过<constructor>标签调用构造器完成赋值,替代反射setter方式。

<resultMap id="UserConstructorMap" type="com.demo.entity.User">
    <constructor>
        <idArg column="id" javaType="Integer"/>
        <arg column="user_name" javaType="String"/>
        <arg column="age" javaType="Integer"/>
    </constructor>
</resultMap>

(三)高级关联映射(多表/嵌套对象)

适用于多表关联查询,封装嵌套对象、集合属性,解决一对一、一对多、多对多的复杂映射场景。

1. 一对一关联映射(&lt;association&gt;)

用于单个嵌套对象(如用户对应唯一身份证),分为嵌套结果映射(联表查询)和嵌套查询映射(子查询)两种方式。

<!-- 一对一嵌套结果映射(联表查询) -->
<resultMap id="UserCardMap" type="com.demo.entity.User">
    <id column="user_id" property="id"/>
    <result column="user_name" property="userName"/>
    <!-- 关联身份证对象 -->
    <association property="idCard" javaType="com.demo.entity.IdCard">
        <id column="card_id" property="id"/>
        <result column="card_no" property="cardNo"/>
    </association>
</resultMap>

2. 一对多/多对一集合映射(<collection>)

用于集合类型属性(如用户对应多个订单),<collection>标签封装List/Set集合,支持联表和子查询两种模式。

<resultMap id="UserOrderMap" type="com.demo.entity.User">
    <id column="user_id" property="id"/>
    <result column="user_name" property="userName"/>
    <!-- 关联订单集合 -->
    <collection property="orderList" ofType="com.demo.entity.Order">
        <id column="order_id" property="id"/>
        <result column="order_no" property="orderNo"/>
    </collection>
</resultMap>

3. 鉴别器映射(<discriminator>)

根据结果集中的某列值,动态切换映射规则,适配继承关系的实体类(如用户分普通用户/会员用户)。

<resultMap id="UserDiscriminatorMap" type="com.demo.entity.User">
    <id column="id" property="id"/>
    <result column="user_name" property="userName"/>
    <!-- 根据user_type字段动态映射 -->
    <discriminator javaType="Integer" column="user_type">
        <case value="1" resultType="com.demo.entity.NormalUser"/>
        <case value="2" resultType="com.demo.entity.VipUser"/>
    </discriminator>
</resultMap>

三、映射优先级与关键补充

  • 优先级:手动配置的resultMap > 自动映射(resultType),同名字段以手动映射为准。
  • 类型处理器(TypeHandler):自定义TypeHandler可解决特殊类型转换(如JSON字符串转对象、枚举映射)。
  • 性能优化:优先使用联表嵌套结果映射,避免子查询导致的N+1查询问题;大数据量场景慎用集合嵌套映射。

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

MyBatis 文章被收录于专栏

本专栏聚焦Java主流持久层框架MyBatis,从基础搭建到源码原理,系统拆解核心组件、动态SQL、结果映射与缓存机制。助力开发者从入门到精通,掌握高效数据层开发技能,适配电商、金融等复杂业务场景。

全部评论

相关推荐

在传统机器学习和深度学习时代,算法岗和开发岗确实是两种不同的&amp;quot;工种&amp;quot;。算法工程师的核心工作是优化指标。大量实验中90%是无效的,最后能交差的可能就是某次灵光一闪改了几行代码带来的收益。这工作确实是&amp;quot;心累&amp;quot;——投入和产出高度不确定。开发工程师的核心工作则是交付功能。产品经理提了需求,你把它拆成技术方案,写代码、做测试、上线。每一项工作目标明确,工作量可评估,产出可衡量。累归累,但至少是&amp;quot;确定性的累&amp;quot;。🌟这一切的前提是:AI的核心生产力瓶颈在&amp;quot;模型&amp;quot;上。✅2023年之后,这个前提被大模型彻底改写了。当GPT、Claude、Qwen这些强大的基础模型变成了可以调用的API,当大部分业务场景不再需要你从头训练一个模型的时候,&amp;quot;做AI&amp;quot;这件事的重心就不可逆转地从模型侧转向了应用侧。&amp;quot;AI算法岗&amp;quot;和&amp;quot;AI开发岗&amp;quot;的要求越来越趋同:都要会Python,都要懂LangChain或类似框架,都要理解RAG和Agent,都要能写提示词,都要能把东西部署上去。Agent不再是实验室里q的概念验证了,它正在成为企业AI落地的主流形态。企业需要的是一个能从业务需求出发,设计Agent架构、编排工作流、接入外部工具、管理上下文、监控Agent行为、持续优化效果的综合性人才。这种人才有一个越来越明确的名字:Agent&nbsp;Engineer。✅建议:无论你现在的title是算法工程师还是开发工程师,都建议你去了解Agent的架构设计、多Agent编排、MCP协议、可观测性这些东西。这不是在追概念,而是这确实是当下和未来一段时间AI应用的主要形态。对于想求职算法岗的同学,如果想参加高质量项目辅导,提升面试能力,欢迎后台联系。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务