API Criteria: List fetch returns a repeating core entity

I have the following Entities; The ticket contains set 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; } 

I load tickets and get attributes. All attributes of 0.1 are not a problem. For workOrders, I used this answer to get the following code.

 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(); 

As a result, in the request, which should return 1 ticket to me with 5 workOrders, I get the same ticket 5 times.

If I just do workOrders Eager Fetch and delete the extraction code, it works as it should.

Can anyone help me? Thanks in advance.

UPDATE:

One explanation of why I'm not just happy with JB Nizet's answer (even if it works in the end).

When I just make relationships impatient, the JPA examines the exact same data as when I make it lazy and add a fetch clause to the / JPQL criterion. The relationships between the various elements are also clear as I am defining a ListAttribute for a Criteria request.

Is there any reasonable explanation for the reason that the JPA does not return the same data in both cases?

UPDATE FOR BOUNTY: although JB Nizet's answer resolved the problem, I still find it pointless that, given the two operations with the same value ("Get Ticket and fetching all WorkOrder inside ticket.workOrders "), perform them using the downloaded download does not require any additional changes, while the DISTINCT command is required to specify a selection

+8
jpa criteria-api
source share
2 answers
  • There is a difference between an active download and receive connection. Unwanted loading does not mean that the data is being loaded in a single request. It just means that it loads immediately, although on additional requests.

  • Criteria are always translated into an SQL query. If you specify joins, this will be a join in SQL. By the nature of SQL, this also multiplies the data of the root entity, which leads to what you got. (Note that you get the same instance multiple times, so the root object does not multiply in memory.)

There are several solutions:

  • use distinct(true)
  • Use a separate root noun. transformer ( .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) ).
  • If you do not need to filter child properties, avoid joining
  • When you need to filter child properties, filter out the subquery ( DetachedCriteria ).
  • Optimize N + 1 Problem Using Batch Size
+20
source share

Have you tried calling distinct(true) in CriteriaQuery?

JPA Specification 2, p. 161, states:

The DISTINCT keyword is used to indicate that duplicate values ​​should be excluded from the query result.

If DISTINCT is not specified, duplicate values ​​are not eliminated.

The javadoc also says:

Specify whether duplicate query results are deleted. value will eliminate duplicates. A value of false will cause duplicates to be saved. If clarity is not indicated, duplicate results should be retained.

The reason you don't need clarity when actively loading the association is probably because the association is not loading using the fetch join, but using an additional request.

+4
source share

All Articles