JPA 条件搜索 属性 在带有子类的抽象集合中

JPA criteria search property in abstract collection with subclasses

我正在使用 Spring 数据规范编写使用 JPA 标准的标准查询 API。

我有一个名为 Thing 的 class,它具有 Set<Characteristic> 属性。
class Characteristic 是一个 抽象 class,它具有 id 和一些共享的基本属性。

然后我有几个具体的 class 扩展 Characteristic 并定义 value 属性。

每个 Thing 在他的 characteristics 集合中可以有 0 个或多个 Characteristic 任何具体类型。

在数据库中,class 层次结构是这样存储的(5 个表):

我需要使用 JPA 条件 API 来搜索至少具有指定值的特征的所有事物。

我已经写了一个 SQL 查询的草稿,我想用 JPA 标准重现 API :

SELECT DISTINCT thing.id
FROM   thing 
       LEFT OUTER JOIN thing_has_characteristic has_c 
                    ON ( has_c.thing_id = thing.id ) 
       LEFT OUTER JOIN characteristic c 
                    ON ( c.id = has_c.characteristic_id ) 
       LEFT OUTER JOIN integer_characteristic integer_c 
                    ON ( integer_c.characteristic_id = c.id ) 
       LEFT OUTER JOIN string_characteristic string_c 
                    ON ( string_c.characteristic_id = c.id ) 
       LEFT OUTER JOIN boolean_characteristic boolean_c 
                    ON ( boolean_c.characteristic_id = c.id ) 
       LEFT OUTER JOIN decimal_characteristic decimal_c 
                    ON ( decimal_c.characteristic_id = c.id ) 
WHERE  integer_c.value = "9694" 
        OR string_c.value = "9694"
        OR decimal_c.value = "9694" 
        OR boolean_c.value = "9694";

当试图将其转换为 JPA 标准时,我陷入了困境,因为我认为我需要从特征集中构建一个子查询来区分四种类型的特征 classes有。

现在,我尝试了一个仅包含整数和字符串类型的小查询,但我对如何使其与特征的子class 层次结构一起工作感到困惑。

private Specification<Thing> buildSearchSpecificationByCharacteristicValue(String value) {

    return (Specification<Thing>) (root, query, builder) -> {

        SetJoin<Thing,IntegerCharacteristic> integers = root.<Thing,IntegerCharacteristic>joinSet("characteristics", JoinType.LEFT );
        Predicate isInteger;
        try{
            isInteger = builder.equal(integers.get("value"), Integer.parseInt(value));
        }catch(NumberFormatException e){
            isInteger = builder.disjunction();
        }

        SetJoin<Thing,StringCharacteristic> strings = root.<Thing,StringCharacteristic>joinSet("characteristics", JoinType.LEFT);
        Predicate isString = builder.equal(strings.get("value"), value);

        return builder.or(
            isInteger,
            isString
        );
    };
}

它产生以下错误:

org.springframework.dao.InvalidDataAccessApiUsageException: 
Unable to locate Attribute  with the the given name [value] on this 
ManagedType [com.xxxxxxxx.common.domain.DomainObject];
 nested exception is java.lang.IllegalArgumentException: 
Unable to locate Attribute  with the the given name [value] 
on this ManagedType [com.xxxxxxxx.common.domain.DomainObject]

好的,我找到了解决问题的方法:

这是一个仅针对整数类型的标准示例,但它隐含了如何为其他类型执行此操作。

return (Specification<Thing>) (root, query, builder) -> {

    Path<Characteristic> characteristics = root.join("characteristics", JoinType.LEFT);

    query.distinct(true);

    Subquery<IntegerCharacteristic> integerSub = query.subquery(IntegerCharacteristic.class);
    Root integerRoot = integerSub.from(IntegerCharacteristic.class);
    integerSub.select(integerRoot);
    integerSub.where(builder.equal(integerRoot.get("value"),Integer.parseInt(value)));

    return builder.in(characteristics).value(integerSub);

};