复杂继承映射
复杂继承映射处理 Java 类层次结构与数据库表之间的映射关系,核心是通过 discriminator 根据列值动态选择不同子类的映射规则。
继承映射三种策略
| 策略 | 表结构 | 优点 | 缺点 |
|---|---|---|---|
| 单表继承 (STI) | 一张表包含所有字段 | 查询简单,无 JOIN | 存在大量 NULL 字段 |
| 类表继承 (CTI) | 父类表 + 子类表 | 无冗余字段 | 查询需 JOIN |
| 具体表继承 (CCTI) | 每个子类一张完整表 | 结构清晰 | 公共字段重复,多态查询困难 |
单表继承映射 (STI)
所有子类字段存储在一张表中,通过 discriminator 列区分类型:
SQL
CREATE TABLE shape (
id INT PRIMARY KEY,
type VARCHAR(20) NOT NULL,
color VARCHAR(20),
-- Circle 专用
radius DOUBLE,
-- Rectangle 专用
width DOUBLE,
height DOUBLE,
-- Triangle 专用
base DOUBLE,
side_a DOUBLE,
side_b DOUBLE
);
Java 类层次:
Java
public abstract class Shape {
private Integer id;
private String color;
}
public class Circle extends Shape {
private Double radius;
}
public class Rectangle extends Shape {
private Double width;
private Double height;
}
public class Triangle extends Shape {
private Double base;
private Double sideA;
private Double sideB;
}
映射配置:
XML
<resultMap id="shapeResultMap" type="Shape">
<id property="id" column="id"/>
<result property="color" column="color"/>
<discriminator javaType="string" column="type">
<case value="CIRCLE" type="Circle">
<result property="radius" column="radius"/>
</case>
<case value="RECTANGLE" type="Rectangle">
<result property="width" column="width"/>
<result property="height" column="height"/>
</case>
<case value="TRIANGLE" type="Triangle">
<result property="base" column="base"/>
<result property="sideA" column="side_a"/>
<result property="sideB" column="side_b"/>
</case>
</discriminator>
</resultMap>
<select id="selectAllShapes" resultMap="shapeResultMap">
SELECT * FROM shape
</select>
单表继承查询简单,一条 SQL 即可获取所有类型数据,但字段稀疏时浪费存储空间。
类表继承映射 (CTI)
父类字段存入基表,子类特有字段存入各自扩展表:
SQL
CREATE TABLE shape_base (
id INT PRIMARY KEY,
type VARCHAR(20) NOT NULL,
color VARCHAR(20)
);
CREATE TABLE circle_ext (
shape_id INT PRIMARY KEY,
radius DOUBLE,
FOREIGN KEY (shape_id) REFERENCES shape_base(id)
);
CREATE TABLE rectangle_ext (
shape_id INT PRIMARY KEY,
width DOUBLE,
height DOUBLE,
FOREIGN KEY (shape_id) REFERENCES shape_base(id)
);
映射配置使用 discriminator + 嵌套 association:
XML
<resultMap id="shapeCTIResultMap" type="Shape">
<id property="id" column="id"/>
<result property="color" column="color"/>
<discriminator javaType="string" column="type">
<case value="CIRCLE" type="Circle">
<association property="radius" column="shape_id"
select="selectCircleExt"/>
</case>
<case value="RECTANGLE" type="Rectangle">
<association property="width" column="shape_id"
select="selectRectangleExt"/>
</case>
</discriminator>
</resultMap>
<!-- 更优方案:JOIN 查询 + columnPrefix -->
<resultMap id="shapeCTIJoinResultMap" type="Shape">
<id property="id" column="id"/>
<result property="color" column="color"/>
<discriminator javaType="string" column="type">
<case value="CIRCLE" type="Circle">
<result property="radius" column="radius"/>
</case>
<case value="RECTANGLE" type="Rectangle">
<result property="width" column="width"/>
<result property="height" column="height"/>
</case>
</discriminator>
</resultMap>
<select id="selectShapesCTI" resultMap="shapeCTIJoinResultMap">
SELECT s.id, s.type, s.color,
c.radius, r.width, r.height
FROM shape_base s
LEFT JOIN circle_ext c ON s.id = c.shape_id
LEFT JOIN rectangle_ext r ON s.id = r.shape_id
</select>
深度继承层次映射
处理多层继承关系时,使用嵌套 discriminator 或组合映射:
Java
public class Vehicle {
private Integer id;
private String brand;
}
public class Car extends Vehicle {
private Integer doorCount;
}
public class ElectricCar extends Car {
private Double batteryCapacity;
private Integer chargingTime;
}
public class GasCar extends Car {
private Double engineDisplacement;
private Integer cylinderCount;
}
XML
<resultMap id="vehicleResultMap" type="Vehicle">
<id property="id" column="id"/>
<result property="brand" column="brand"/>
<discriminator javaType="string" column="type">
<case value="CAR" type="Car">
<result property="doorCount" column="door_count"/>
<discriminator javaType="string" column="energy_type">
<case value="ELECTRIC" type="ElectricCar">
<result property="batteryCapacity" column="battery_capacity"/>
<result property="chargingTime" column="charging_time"/>
</case>
<case value="GAS" type="GasCar">
<result property="engineDisplacement" column="engine_disp"/>
<result property="cylinderCount" column="cylinder_count"/>
</case>
</discriminator>
</case>
</discriminator>
</resultMap>
嵌套 discriminator 适用于三层及以上继承,但会增加映射复杂度,建议控制继承层级不超过三层。
多态查询实践
统一查询接口
Java
public interface ShapeMapper {
// 多态查询:返回 Shape 及其所有子类
List<Shape> selectAllShapes();
// 按类型过滤
List<Shape> selectShapesByType(@Param("types") List<String> types);
// 获取特定子类
List<Circle> selectCircles();
}
XML
<select id="selectAllShapes" resultMap="shapeResultMap">
SELECT * FROM shape
</select>
<select id="selectShapesByType" resultMap="shapeResultMap">
SELECT * FROM shape
<where>
<if test="types != null and types.size() > 0">
type IN
<foreach collection="types" item="t" open="(" separator="," close=")">
#{t}
</foreach>
</if>
</where>
</select>
多态更新操作
XML
<!-- 更新公共字段 -->
<update id="updateShapeColor">
UPDATE shape SET color = #{color} WHERE id = #{id}
</update>
<!-- 更新子类特有字段 -->
<update id="updateCircleRadius">
UPDATE shape SET radius = #{radius} WHERE id = #{id} AND type = 'CIRCLE'
</update>
更新操作需根据 type 列区分处理逻辑,避免将子类字段更新到错误的记录上。
策略对比与选型
| 维度 | 单表继承 | 类表继承 | 具体表继承 |
|---|---|---|---|
| 查询性能 | 最优(单表) | 中(需 JOIN) | 差(多表 UNION) |
| 存储空间 | 浪费(NULL 多) | 最优 | 中 |
| 扩展子类 | 加列即可 | 新建扩展表 | 新建完整表 |
| 多态查询 | 支持 | 支持 | 困难 |
| 映射复杂度 | 低 | 中 | 高 |
| 适用场景 | 字段重叠率高 | 子类差异大 | 完全独立业务 |
选型建议:
- 字段重叠率 > 60%:单表继承,查询效率最优。
- 子类特有字段多且差异大:类表继承,避免大量 NULL。
- 子类业务完全独立:具体表继承,保持边界清晰。
要点总结
- 单表继承 (STI) 查询最优但存在字段稀疏问题,适合重叠率高的场景。
- 类表继承 (CTI) 通过 JOIN 扩展表获取子类字段,结构清晰但查询略慢。
- discriminator 支持嵌套使用,可处理三层及以上继承关系。
- 嵌套 discriminator 复杂度随层级增长,建议控制继承深度不超过三层。
- 多态查询配合 discriminator 可统一返回父类引用,运行时自动实例化为子类。
- 策略选型核心指标:字段重叠率决定 STI 还是 CTI,业务独立性决定 CCTI。
📝 发现内容有误?点击此处直接编辑