JPA Criteria查询与孩子不工作的谓词的一对多关系

时间:2016-04-06 13:42:12

标签: java jpa

我有一个父实体

@Entity
@Table(name = "tblmodule", uniqueConstraints = {
    @UniqueConstraint(columnNames = "name")
})
@SequenceGenerator(name = ACLEntityConstant.SEQ_MODULE, initialValue = 1, allocationSize = 100)
public class Module implements BaseEntity {

    private static final long serialVersionUID = -4924925716441434537L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = ACLEntityConstant.SEQ_MODULE)
    private Long id;

    @Column
    private String name;

    @Column
    private String displayName;

    @OneToMany(mappedBy = "module")
    @JsonManagedReference
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<SubModule> subModule;

    //getters and setters
}

模块与SubModule有一对多关系,实体类如下

    @Entity
@Table(name = "tblsubmodule", uniqueConstraints = {
    @UniqueConstraint(columnNames = {"id", "name"})
})
@SequenceGenerator(name = ACLEntityConstant.SEQ_SUBMODULE, initialValue = 1, allocationSize = 100)
public class SubModule implements BaseEntity {

    private static final long serialVersionUID = 5452251016263080356L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = ACLEntityConstant.SEQ_SUBMODULE)
    private Long id;

    @Column
    private String name;

    @Column
    private String displayName;

    @ManyToOne
    @JsonBackReference
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private Module module;
}

我正在尝试使用条件查询找到具有特定名称(&#39; submodule1&#39;)的子模块的模块。这是我的代码:

    CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
    CriteriaQuery cq = cb.createQuery();
    Root root = cq.from(Module.class);
    cq.select(root);
    ListJoin join = root.joinList("subModule");
    cq.where(cb.equal(join.get("name"), "submodule1"));
    cq.distinct(true);
    cq.groupBy(root.get("name"));
    TypedQuery query = getEntityManager().createQuery(cq);
    return query.getResultList();

使用此代码,它为我提供了模块中的所有子模块。

有没有办法只选择名称为子模块的子模块&#39;? 谢谢

2 个答案:

答案 0 :(得分:2)

您可以返回包含名称= somevalue的子模块的所有模块,但在JPA中无法获取&#39; module&#39;具有由原始查询中的某些内容过滤的子模块列表的实例 - JPA需要返回反映数据库中状态的托管模块实例,以便它可以管理您可能进行的任何更改。

而不是使用对象中设置的module.submodules,直接使用类似于"Select submodule from SubModule submodule where submodule.name = :name"的查询查询子模块。然后,您可以使用结果基于submodule.module创建子模块集合的映射。

答案 1 :(得分:2)

表A

A_IdPKey)   name    age

1            X       34

表B

B_id(PK)    A_id(FrnKey)    salary  
11             1             2000   
22             1             3000   

使用其Criteria构建器时: select a.*,b.* from A a, B b where a.A_id=b.B_id and a.name='X' and b.salary=2000

通过上述查询,我​​们应该得到字段:salary=2000

A.A_id  A.name  A.age   B.B_id  B.salary    
1          X    34       11     2000

但在JPA中,对于@OneToMany关系,Query首先在master上触发,所有Criteria和fetches只是主记录。 稍后单独触发查询,但B表标准将不包含所有条件,而是仅包含一个条件,即仅使用一个条件b.A_id=[primarykeyfetched from master]

获取的主记录的主键VALUE

select B.* from Table B join Table A where B.A_id=(for individual Ids)

这就是Backend中发生的事情,因此无论薪水不匹配,它都会获取所有B匹配A_id的{​​{1}}条记录。我们正在获取

A.A_id  A.name  A.age   B.B_id  B.salary    B.dept
1        X      34       11     2000        IT
1        X      34       22     3000        IT

解决方案很简单:创建一个独立的类,我们需要@OneToMany关系字段。

第1步

public class AB {

    private A a;
    private B b;  //@OneToMany with A in the Entity class definitions

    public AB(){}

    public AB(A a, B b){ ///Very Important to have this constructor
        this.a=a;
        this.b=b;
    }

    getters...

    setters....

}

第2步

CriteriaQuery<AB> q = criteriaBuilder.createQuery(AB.class);
Root<A> fromA = criteriaQuery.from(A.class);      
List<Predicate> predicates = new ArrayList<Predicate>();         
Join<A,B> fromB = fromA.join("b", JoinType.INNER);/*"b",fieldName of B in entity Class A*/
criteriaQuery.select(criteriaBuilder.construct(AB.class,A,B));//looks AB's 2 param constr.
predicates.add(criteriaBuilder.equal(fromA.get("name"), filter.getName()));   
predicates.add(criteriaBuilder.equal(fromB.get("salary"), filter.getSalary()));
.......... 
List<AB> ABDataList = typedQuery.getResultList();  
for(AB eachABData :ABDataList){
    ....put in ur objects
    obj.setXXX(eachABData.getA().getXXX());
    obj.setYYY(eachABData.getB().getYYY());
} 

这将为主表A提供应用所有条件的所有结果,并比较表B的主键而不是外键。