过滤器连接多个表的规范,包括嵌入式主键
Specification for filter joining multiple tables, including embedded primary key
我想构建一个Specification
来过滤一个属性,它没有直接包含在实体中,而是在“第二次加入”中。
实体看起来像这样(大大简化):
@Entity
public class A {
@Id
private UUID id;
@OneToMany(mappedBy=pk.a)
private List<B> b;
}
@Entity
public class B {
@EmbeddedId
private PK pk;
}
@Embeddable
public class PK {
@ManyToOne
private A a;
@ManyToOne
private C c;
}
@Entity
public class C {
@Id
private UUID id;
@OneToMany(mappedBy=pk.b)
private List<B> b;
}
我想获取所有具有特定 C.id 的 A,并且我已经有一个按名称命名的存储库方法正在运行:
List<A> findAllByBPKCId(String id);
因为我想要更多的过滤器,所以我需要移动到 JpaSpecificationExecutor
,但是我无法构建 Specification
来执行与以前的存储库方法相同的操作。
到目前为止我尝试的是:
打电话
List<A> findAll(Specification specification);
和
new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteraBuilder) {
return criteriaBuilder.equal(root.join("b").join(pk.c).get("id"), ID_TO_COMPARE);
}
}
但是它找不到“pk.c”,我需要先以某种方式“获取”pk,然后再加入 c。它抛出 IllegalArgumentException 因为它是 'Unable to locate Attribute with the given name [pk.b]'.
如何加入嵌套在嵌入式“复合主键”中的实体?
您需要先加入B
和PK
,然后再加入C
,即PK
应该与其他实体一样考虑加入时
请考虑以下代码:
new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteraBuilder) {
Join<A, B> joinAB = root.join("b");
Join<B, PK> joinBPK = joinAB.join("pk");
Join<PK, C> joinPKC = joinBPK.join("c");
return criteraBuilder.equal(joinPKC.get("id"), cb.literal(ID_TO_COMPARE));
}
}
可以使用 lambdas 简化此代码:
public static Specification<A> getSpecification(String ID_TO_COMPARE) {
return (root, query, cb) -> {
Join<A, B> joinAB = root.join("b");
Join<B, PK> joinBPK = joinAB.join("pk");
Join<PK, C> joinPKC = joinBPK.join("c");
return cb.equal(joinPKC.get("id"), cb.literal(ID_TO_COMPARE));
};
}
此外,如果有机会,使用criteria metamodel,它在构造查询时提供编译类型字段检查并使事情变得清晰:
public static Specification<A> getSpecification(String ID_TO_COMPARE) {
return (root, query, cb) -> {
Join<A, B> joinAB = root.join(A_.b);
Join<B, PK> joinBPK = joinAB.join(B_.pk);
Join<PK, C> joinPKC = joinBPK.join(PK_.c);
return cb.equal(joinPKC.get(C_.id), cb.literal(ID_TO_COMPARE));
};
}
我想构建一个Specification
来过滤一个属性,它没有直接包含在实体中,而是在“第二次加入”中。
实体看起来像这样(大大简化):
@Entity
public class A {
@Id
private UUID id;
@OneToMany(mappedBy=pk.a)
private List<B> b;
}
@Entity
public class B {
@EmbeddedId
private PK pk;
}
@Embeddable
public class PK {
@ManyToOne
private A a;
@ManyToOne
private C c;
}
@Entity
public class C {
@Id
private UUID id;
@OneToMany(mappedBy=pk.b)
private List<B> b;
}
我想获取所有具有特定 C.id 的 A,并且我已经有一个按名称命名的存储库方法正在运行:
List<A> findAllByBPKCId(String id);
因为我想要更多的过滤器,所以我需要移动到 JpaSpecificationExecutor
,但是我无法构建 Specification
来执行与以前的存储库方法相同的操作。
到目前为止我尝试的是: 打电话
List<A> findAll(Specification specification);
和
new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteraBuilder) {
return criteriaBuilder.equal(root.join("b").join(pk.c).get("id"), ID_TO_COMPARE);
}
}
但是它找不到“pk.c”,我需要先以某种方式“获取”pk,然后再加入 c。它抛出 IllegalArgumentException 因为它是 'Unable to locate Attribute with the given name [pk.b]'.
如何加入嵌套在嵌入式“复合主键”中的实体?
您需要先加入B
和PK
,然后再加入C
,即PK
应该与其他实体一样考虑加入时
请考虑以下代码:
new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteraBuilder) {
Join<A, B> joinAB = root.join("b");
Join<B, PK> joinBPK = joinAB.join("pk");
Join<PK, C> joinPKC = joinBPK.join("c");
return criteraBuilder.equal(joinPKC.get("id"), cb.literal(ID_TO_COMPARE));
}
}
可以使用 lambdas 简化此代码:
public static Specification<A> getSpecification(String ID_TO_COMPARE) {
return (root, query, cb) -> {
Join<A, B> joinAB = root.join("b");
Join<B, PK> joinBPK = joinAB.join("pk");
Join<PK, C> joinPKC = joinBPK.join("c");
return cb.equal(joinPKC.get("id"), cb.literal(ID_TO_COMPARE));
};
}
此外,如果有机会,使用criteria metamodel,它在构造查询时提供编译类型字段检查并使事情变得清晰:
public static Specification<A> getSpecification(String ID_TO_COMPARE) {
return (root, query, cb) -> {
Join<A, B> joinAB = root.join(A_.b);
Join<B, PK> joinBPK = joinAB.join(B_.pk);
Join<PK, C> joinPKC = joinBPK.join(PK_.c);
return cb.equal(joinPKC.get(C_.id), cb.literal(ID_TO_COMPARE));
};
}