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

时间:2016-03-24 11:24:12

标签: jpa inheritance collections spring-data criteria

我正在使用Spring数据规范来编写使用JPA条件API的条件查询。

我有一个名为Thing的班级,其Set<Characteristic>属性。{ 课程Characteristic是一个抽象类,其中包含id和一些共享的基本属性。

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

  • IntegerCharacteristic value属性为Integer
  • DecimalCharacteristic value属性为Double
  • StringCharacteristic value属性为String
  • BooleanCharacteristic value属性为Boolean

每个ThingCharacteristic集合中可以包含任意具体类型的0个或多个characteristics

在数据库中,类层次结构与连接的继承策略一起存储(5个表):

  • 特征(id +一些常见字段)
  • integer_characteristic(外键+ INT值)
  • decimal_characteristic(外键+ DECIMAL值)
  • string_characteristic(外键+ VARCHAR值)
  • boolean_characteristic(外键+ TINYINT值)

我需要使用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标准时,我会陷入困境,因为我认为我需要从特征集中构建一个子查询来区分我所拥有的四种类型的特征类。

现在,我尝试了一个只有Integer和String类型的小查询,但我对如何使它与特征的子类层次结构一起工作感到困惑。

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]

1 个答案:

答案 0 :(得分:2)

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

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

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);

};