JPA 条件搜索 属性 在带有子类的抽象集合中
JPA criteria search property in abstract collection with subclasses
我正在使用 Spring 数据规范编写使用 JPA 标准的标准查询 API。
我有一个名为 Thing
的 class,它具有 Set<Characteristic>
属性。
class Characteristic
是一个 抽象 class,它具有 id
和一些共享的基本属性。
然后我有几个具体的 class 扩展 Characteristic
并定义 value
属性。
IntegerCharacteristic
value
属性是 Integer
DecimalCharacteristic
value
属性是 Double
StringCharacteristic
value
属性是 String
BooleanCharacteristic
value
属性是 Boolean
每个 Thing
在他的 characteristics
集合中可以有 0 个或多个 Characteristic
任何具体类型。
在数据库中,class 层次结构是这样存储的(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 标准时,我陷入了困境,因为我认为我需要从特征集中构建一个子查询来区分四种类型的特征 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);
};
我正在使用 Spring 数据规范编写使用 JPA 标准的标准查询 API。
我有一个名为 Thing
的 class,它具有 Set<Characteristic>
属性。
class Characteristic
是一个 抽象 class,它具有 id
和一些共享的基本属性。
然后我有几个具体的 class 扩展 Characteristic
并定义 value
属性。
IntegerCharacteristic
value
属性是Integer
DecimalCharacteristic
value
属性是Double
StringCharacteristic
value
属性是String
BooleanCharacteristic
value
属性是Boolean
每个 Thing
在他的 characteristics
集合中可以有 0 个或多个 Characteristic
任何具体类型。
在数据库中,class 层次结构是这样存储的(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 标准时,我陷入了困境,因为我认为我需要从特征集中构建一个子查询来区分四种类型的特征 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);
};