MyBatis SQL结果封装原理及映射形式详解
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
一、MyBatis结果封装核心原理
MyBatis本质是通过JDBC结果集处理+Java反射+类型转换完成SQL结果到Java对象的映射,核心流程分为5步,全程无侵入式封装,兼顾灵活性与易用性:
- 执行SQL获取结果集:MyBatis通过Executor执行JDBC操作,调用Statement获取ResultSet结果集,这是封装的数据源。
- 解析映射规则:根据Mapper接口的返回值类型、XML/注解中的映射配置(resultType/resultMap),确定目标对象结构与字段对应关系。
- 遍历ResultSet行数据:逐行读取结果集数据,每一行对应一个目标对象实例,多行数据则组装为List/Set等集合。
- 字段映射与类型转换:通过TypeHandler完成数据库类型(如VARCHAR/INT/DATETIME)到Java类型(String/Integer/LocalDateTime)的转换,再通过反射赋值给对象属性。
- 组装返回对象:单行数据直接返回目标对象,多行数据封装为集合返回;关联查询则递归完成嵌套对象/集合的组装。
核心亮点: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. 一对一关联映射(<association>)
用于单个嵌套对象(如用户对应唯一身份证),分为嵌套结果映射(联表查询)和嵌套查询映射(子查询)两种方式。
<!-- 一对一嵌套结果映射(联表查询) -->
<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:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
本专栏聚焦Java主流持久层框架MyBatis,从基础搭建到源码原理,系统拆解核心组件、动态SQL、结果映射与缓存机制。助力开发者从入门到精通,掌握高效数据层开发技能,适配电商、金融等复杂业务场景。
