JPA Criteria查询以使用EntityGraph从主表和子表中获取数据

时间:2020-05-27 14:51:38

标签: java hibernate jpa spring-data-jpa eclipselink

我正尝试使用JPA Criteria查询基于过滤条件从父级到子级获取数据,这样可以避免对DB的多次查询,但无法获得所需的结果。以下是我的示例实体(没有获取器/设置器)

@Entity
public class ParentTable implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ID")
    private String id;

    @Column(name = "KEY_COLUMN",length = 30)
    private String keyColumn;

    @Column(name = "CODE",length = 30)
    private String code;

    @Column(name = "KEY_DESC",length = 240)
    private String desc;

    @OneToMany(mappedBy = "parentTable",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<ChildTable> childTableList;
}


@Entity
public class ChildTable implements Serializable{

    private static final long serialVersionUID = 1L;

    public ChildTable() {
        super();
    }

    @Id
    @Column(name = "ID",length = 80)
    private String id;

    @Column(name = "PARENT_KEY_COLUMN",length = 30,insertable = false,updatable = false)
    private String parentKeyColumn;

    @Column(name = "CHILD_CODE",length = 30)
    private String childCode;

    @Column(name = "CHILD_DESC",length = 240)
    private String chldDesc;


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "PARENT_KEY_COLUMN", referencedColumnName = "KEY_COLUMN")
    private ParentTable parentTable;

}

条件构建器代码段-

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ParentTable> query = cb.createQuery(ParentTable.class);
Root<ParentTable> fromParent = query.from(ParentTable.class);
Join<ParentTable, ChildTable> details = fromParent.join("childTableList");

List<Predicate> conditions = new ArrayList();
conditions.add(cb.equal(details.get("childCode"), childCode));
conditions.add(cb.equal(details.get("chldDesc"),chldDesc));

TypedQuery<ParentTable> typedQuery = em.createQuery(query.select(fromParent).where(conditions.toArray(new Predicate[] {})));
List<ParentTable> parentTableList = typedQuery.getResultList();

这将执行并仅给出父表的结果,如果我获取子表数据,可以再次看到JPA查询被执行,是否可以避免这种情况并获取匹配3个动态参数的子实体列表? 1. ParentTable.code,2. ChildTable.childCode,3. ChildTable.chldDesc。

有人能帮助我构造如下的JPA查询吗?它在一个数据库命中中执行,而不是在上面的代码片段中多次往返执行?

select * from ParentTable p,ChildTable c where p.KEY_COLUMN=c.PARENT_KEY_COLUMN and p.CODE=? and c.CHILD_CODE=? and c.CHILD_DESC=?

更新:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<ParentTable> cq = builder.createQuery(ParentTable.class);
Root<ParentTable> root = cq.from(ParentTable.class);
Join<ParentTable, ChildTable> join = root.join("childTableList");

Predicate p1=builder.equal(root.get("code"), "code");
Predicate p2=builder.like(join.get("chldDesc"), "%chldDesc%");
Predicate p3=builder.equal(join.get("childCode"), "childCode");
Predicate andPredicate = builder.and(p1,p2, p3);
cq.select(root).where(andPredicate);

EntityGraph<ParentTable> fetchGraph = entityManager.createEntityGraph(ParentTable.class);
fetchGraph.addSubgraph("childTableList");
List<ParentTable> parentTableList=entityManager.createQuery(cq).setHint("javax.persistence.loadgraph", fetchGraph).getResultList();
parentTableList.forEach(System.out::println);

这种方法形成了上述期望的查询,但是又形成了一个如下所示的查询,为什么即使不需要第二个查询也被触发?

select * from ParentTable  parentTable0_ where parentTable0_.code=?

1 个答案:

答案 0 :(得分:0)

需要JPA才能为您提供反映数据库中数据的管理实体结果。您放在查询上的筛选器不筛选内部关系。

您的'join'子句仅影响应用于返回的ParentTable实体的过滤器。返回的每个实体将具有完整的'childTableList'集合,因此,即使您返回的父表实例具有特定的代码和描述,childTableList也会显示其所有子级。那就是您的JPA,并且特定的提供程序确实可以过滤这些映射的集合(AdditionalCriteria)IMO,这是一条存在许多问题的糟糕路线。

如果要具有与特定代码和描述匹配的childTableEntries,则查询应采用以下形式(使用JPQL)

"Select c, p from ChildTable c join c.parentTable p where c.childCode = :code and c.chldDesc = :desc"

这将为您返回一个List结果,其中Object数组为匹配的每一行具有子项和父项。因此,如果一个人有不止一个匹配的孩子,则重复父母。

否则,额外的查询是由访问parentTable条目上的childTableList引起的,因为它们被标记为惰性。您的条件查询指定了“ join”,因为您想在过滤parentTable实体时使用childTableList条目。如果希望将childTableList与parentTables一起获取,则需要使用fetchJoins。根实现FetchParent,除了您已定义的联接之外,它还允许您在childTableList上指定一个“提取”。在JPQL中,类似:

 "select p from ParentTable p fetch join p.childTableList, join p.childTableList c where c.childCode = :code and c.chldDesc = :desc"