Criteria API:获取列表返回重复的主实体

时间:2012-06-14 11:01:11

标签: jpa jpa-2.0 criteria-api

我有以下实体;故障单包含一组0,N WorkOrder:

@Entity
public class Ticket {

  ...

  @OneToMany(mappedBy="ticket", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private List<WorkOrder> workOrders = null;

  ...
}

@Entity
public class WorkOrder {
  ...
  @ManyToOne
  @JoinColumn(nullable = false)
  private Ticket ticket;
}

我正在加载Tickets并获取属性。所有0,1属性都没有问题。对于workOrders,我使用this answer来获取以下代码。

CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Ticket> criteriaQuery = criteriaBuilder
  .createQuery(Ticket.class);
Root<Ticket> rootTicket = criteriaQuery.from(Ticket.class);

ListAttribute<? super Ticket, WorkOrder> workOrders =
  rootTicket.getModel().getList("workOrders", WorkOrder.class);
rootTicket.fetch(workOrders, JoinType.LEFT);

    // WHERE logic
    ...

criteriaQuery.select(rootTicket);
TypedQuery<Ticket> query = this.entityManager.createQuery(criteriaQuery);
return query.getResultList();

结果是,在一个应该返回1个Ticket with 5 workOrders的查询中,我正在检索相同的Ticket 5次。

如果我只是让workOrders成为Eager Fetch并删除获取代码,它就可以正常工作。

任何人都可以帮助我吗?提前谢谢。

更新:

关于为什么我对JB Nizet的回答不满意的一个解释(即使它最终有效)。

当我只是急切关系时,JPA正在检查完全相同的数据,当我把它变为懒惰时,将fetch子句添加到Criteria / JPQL。当我为Criteria查询定义ListAttribute时,各种元素之间的关系也很清楚。

对于JPA在两种情况下都没有返回相同数据的原因,有一些合理的解释?

BOUNTY的更新:虽然JB Nizet的答案确实解决了这个问题,但我仍然发现,如果两个具有相同含义的操作(“获取Ticket并在{{1}内获取所有WorkOrder,那将毫无意义“),通过急切加载执行它们不需要进一步更改,而指定提取需要ticket.workOrders命令

2 个答案:

答案 0 :(得分:24)

  1. 预先加载和获取加入之间存在差异。急切加载并不意味着数据在同一查询中加载。它只是意味着它会立即加载,但需要额外的查询。

  2. 标准始终转换为SQL查询。如果指定连接,则它将加入SQL。根据SQL的性质,这也会增加根实体的数据,从而产生您所获得的效果。 (请注意,您多次获得相同的实例,因此根实体不会在内存中相乘。)

  3. 有几种解决方案:

    • 使用distinct(true)
    • 使用不同的根实体转换器(.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY))。
    • 如果您不需要按子属性进行过滤,请避免使用连接
    • 当您需要按子属性进行过滤时,请按子查询(DetachedCriteria)进行过滤。
    • 使用batch-size
    • 优化N + 1问题

答案 1 :(得分:4)

您是否尝试过在CriteriaQuery上调用distinct(true)

JPA 2规范,第161页说:

  

DISTINCT关键字用于指定重复值必须为   从查询结果中删除。

     

如果未指定DISTINCT,则不会消除重复值。

javadoc还说:

  

指定是否删除重复的查询结果。确实如此   值将导致重复被删除。假值会导致   重复保留。如果未指定distinct,   必须保留重复的结果。

在急切加载关联时不需要distinct的原因可能只是使用获取连接加载关联,而是使用其他查询。