Mybatis之ResultMap中的discriminator标签介绍

在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可大幅提升代码的可维护性和灵活性,但需注意避免过度复杂的映射逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值