在MyBatis的 ResultMap 中,可以通过<discriminator>标签(鉴别器)来指定一个列的值,根据这个值的不同,动态地选择不同的 resultMap 进行映射,类似于 Java 中的 switch 语句。当查询结果需要根据某个字段的值映射到不同的子类时,discriminator 特别有用。本文将针对discriminator 进行详细介绍。
1. 核心概念
discriminator通过检查结果集中的某个列(column属性),根据其值(case标签的value属性)选择对应的映射规则:
- 单表继承映射:将基类映射到不同子类。
- 表结构兼容:处理同一列在不同场景下的不同含义。
- 动态映射:根据运行时条件选择不同的映射方式。
2. 关键属性
| 属性 | 作用 |
|---|---|
| column | 用于判断的数据库列名。 |
| javaType | 列值的 Java 类型(如string、int),可选,用于类型转换。 |
| jdbcType | 列的 JDBC 类型(如VARCHAR、INTEGER),可选,用于处理 NULL 值。 |
| typeHandler | 自定义类型处理器,可选,用于特殊类型转换。 |
3. 使用场景
典型应用场景
- 单表继承映射或继承结构映射:根据基类中的类型字段,将结果映射到不同的子类。
- 表结构变化兼容:处理数据库表结构演进时的兼容性问题。
- 动态选择关联映射
示例
3.1 单表继承映射(最典型场景)
假设我们有一个 Vehicle 基类和两个子类 Car、Truck,数据库中通过 type 字段区分车辆类型:
// 基类
public class Vehicle {
private Integer id;
private String brand;
// getters/setters
}
// 子类
public class Car extends Vehicle {
private Integer doorCount;
// getters/setters
}
public class Truck extends Vehicle {
private Integer payload;
// getters/setters
}
数据表结构:
CREATE TABLE vehicles (
id INT PRIMARY KEY,
brand VARCHAR(50),
type VARCHAR(10), -- 'car' 或 'truck'
door_count INT, -- 仅汽车有此属性
payload INT -- 仅卡车有此属性
);
使用discriminator的映射配置:
<resultMap id="vehicleResultMap" type="Vehicle">
<id property="id" column="id" />
<result property="brand" column="brand" />
<!-- 鉴别器:根据type字段决定具体映射 -->
<discriminator column="type" javaType="string">
<!-- 当type为'car'时,额外映射Car类的属性 -->
<case value="car" resultMap="carResultMap" />
<!-- 当type为'truck'时,额外映射Truck类的属性 -->
<case value="truck" resultMap="truckResultMap" />
</discriminator>
</resultMap>
<!-- Car子类的额外映射 -->
<resultMap id="carResultMap" type="Car" extends="vehicleResultMap">
<result property="doorCount" column="door_count" />
</resultMap>
<!-- Truck子类的额外映射 -->
<resultMap id="truckResultMap" type="Truck" extends="vehicleResultMap">
<result property="payload" column="payload" />
</resultMap>
对应的SQL查询语句:
<select id="getVehicleById" parameterType="int" resultMap="vehicleResultMap">
SELECT id, brand, type, door_count, payload
FROM vehicles
WHERE id = #{id}
</select>
执行效果
- 当查询结果的
type字段为 car 时,MyBatis 会返回 Car 对象,包含 doorCount 属性。 - 当
type为 truck 时,返回 Truck 对象,包含 payload 属性。 - 基类 Vehicle 的属性(如 id、brand)会被所有子类继承。
关键点说明
column:指定作为鉴别条件的数据库列。javaType:指定列的 Java 类型。case:value:列值匹配条件(如car、1)。resultMap:当列值匹配时使用的映射(可以是继承或引用其他 resultMap)。type:直接指定映射类型(替代resultMap),需在标签内定义映射规则。
- 继承机制:使用
extends属性复用基类映射,避免重复配置。
3.2 表结构变化兼容
假设历史表中status字段在新版本中有不同含义,通过discriminator兼容:
<resultMap id="orderResultMap" type="Order">
<id property="id" column="id" />
<result property="orderNumber" column="order_number" />
<discriminator column="version" javaType="int">
<!-- 旧版本映射 -->
<case value="1">
<result property="status" column="status" typeHandler="com.example.OldStatusHandler" />
</case>
<!-- 新版本映射 -->
<case value="2">
<result property="status" column="status" typeHandler="com.example.NewStatusHandler" />
</case>
</discriminator>
</resultMap>
3.3 动态选择关联映射
根据用户角色不同,返回不同的关联数据:
<resultMap id="userResultMap" type="User">
<id property="id" column="id" />
<result property="username" column="username" />
<discriminator column="role" javaType="string">
<case value="admin" resultMap="adminResultMap" />
<case value="user" resultMap="normalUserResultMap" />
</discriminator>
</resultMap>
4. 复杂场景扩展
4.1 在中直接定义映射
你还可以在 <case> 中直接定义嵌套映射,而不必引用外部的 resultMap:
<discriminator column="type" javaType="string">
<case value="car">
<result property="doorCount" column="door_count" />
<association property="engine" javaType="Engine">
<result property="model" column="engine_model" />
</association>
</case>
<case value="truck">
<result property="payload" column="payload" />
</case>
</discriminator>
这种方式适合简单的映射场景,但对于复杂继承结构,推荐使用单独的 resultMap 以提高可维护性。
4.2 多级鉴别器
嵌套使用discriminator处理更复杂的条件分支:
<resultMap id="complexResultMap" type="BaseEntity">
<discriminator column="type" javaType="string">
<case value="type1" resultMap="type1ResultMap" />
<case value="type2">
<result property="commonProp" column="common_col" />
<!-- 二级鉴别器 -->
<discriminator column="sub_type" javaType="string">
<case value="sub1" resultMap="type2Sub1ResultMap" />
<case value="sub2" resultMap="type2Sub2ResultMap" />
</discriminator>
</case>
</discriminator>
</resultMap>
5. 注意事项
- 默认映射:若列值不匹配任何,则使用主resultMap的映射规则。
- 继承与复用:通过extends属性复用基类映射,避免重复配置。
- 性能影响:复杂的鉴别逻辑可能影响性能,建议在必要时使用。
- NULL 值处理:若鉴别列值为 NULL,MyBatis 会忽略所有,使用主映射。
(主映射:是 MyBatis 中 ResultMap 的基础组成部分,负责实体类直接属性与数据库字段的一对一映射,不涉及关联对象的处理即需通过<association>,<collection>,<discriminator>标签处理关联映射。)
6. 总结
discriminator是 MyBatis 处理复杂映射的强大工具,核心优势在于:
- 灵活处理继承结构:将单表数据映射到不同子类。
- 兼容表结构变化:在不修改表结构的前提下支持新老版本。
- 动态结果集处理:根据运行时条件选择映射规则。
合理使用discriminator可大幅提升代码的可维护性和灵活性,但需注意避免过度复杂的映射逻辑。

1264

被折叠的 条评论
为什么被折叠?



