【问题标题】:Specification for filter joining multiple tables, including embedded primary key过滤器连接多个表的规范,包括嵌入的主键
【发布时间】:2021-09-10 05:00:58
【问题描述】:

我想构建一个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”,我需要在加入 c 之前先以某种方式“获取”pk。它会引发 IllegalArgumentException,因为它“无法找到具有给定名称 [pk.b] 的属性”。

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

【问题讨论】:

    标签: java hibernate jpa repository hibernate-criteria


    【解决方案1】:

    你需要先加入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));
      };
    }
    

    【讨论】:

    • @Jens 昨天,我修改了答案以包含您的代码而不是 lambdas。你能测试解决方案吗?我对它的正确性很有信心,特别是在使用标准元模型时。它在您的用例中有效吗?
    • 是的,抱歉,我花了几天时间才回到这个话题。它工作得很好,谢谢。 :) 我的问题是,我不知道我可以像实体一样加入 Embeddable,而不是像属性一样获取它。
    • 请您不必道歉。非常欢迎您@Jens。我很高兴听到您能够解决问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-11
    • 1970-01-01
    • 2012-06-11
    • 1970-01-01
    • 2020-09-24
    • 2014-06-11
    • 2020-06-07
    相关资源
    最近更新 更多