过滤器连接多个表的规范,包括嵌入式主键

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]'.

如何加入嵌套在嵌入式“复合主键”中的实体?

您需要先加入BPK,然后再加入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));
  };
}