Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate Criteria returns children multiple times with FetchType.EAGER

Tags:

java

hibernate

People also ask

What is FetchType eager in Hibernate?

EAGER fetching tells Hibernate to get the related entities with the initial query. This can be very efficient because all entities are fetched with only one query. But in most cases it just creates a huge overhead because you select entities you don't need in your use case. You can prevent this with FetchType.

What is FetchType eager in JPA?

EAGER associations when executing a JPQL, Criteria API, or native SQL query that fetched entities. That's why FetchType. EAGER is a dangerous mapping. It forces you to use JOIN FETCH in all your queries that fetch the entity containing the FetchType. EAGER association.

Is ManyToOne eager by default?

By default, the JPA @ManyToOne and @OneToOne annotations are fetched EAGERly, while the @OneToMany and @ManyToMany relationships are considered LAZY. This is the default strategy, and Hibernate doesn't magically optimize your object retrieval, it only does what is instructed to do.

What is the difference between criteria and criterion in Hibernate?

Criteria criteria= session.createCriteria(Order.class) It can be used as restrictions on the criteria query. In other words, Criterion is the object-oriented representation of the “where” clause of a SQL query. The conditions to be applied (also known as restrictions) can be provided by the Restrictions class.


This is actually the expected behaviour if I understood your configuration correctly.

You get the same Order instance in any of the results, but since now you are doing a join with the OrderTransaction, it has to return the same amount of results a regular sql join will return

So actually it should apear multiple times. this is explained very well by the author (Gavin King) himself here: It both explains why, and how to still get distinct results


Also mentioned in the Hibernate FAQ :

Hibernate does not return distinct results for a query with outer join fetching enabled for a collection (even if I use the distinct keyword)? First, you need to understand SQL and how OUTER JOINs work in SQL. If you do not fully understand and comprehend outer joins in SQL, do not continue reading this FAQ item but consult a SQL manual or tutorial. Otherwise you will not understand the following explanation and you will complain about this behavior on the Hibernate forum.

Typical examples that might return duplicate references of the same Order object:

List result = session.createCriteria(Order.class)
                    .setFetchMode("lineItems", FetchMode.JOIN)
                    .list();

<class name="Order">
    ...
    <set name="lineItems" fetch="join">

List result = session.createCriteria(Order.class)
                       .list();
List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();

All of these examples produce the same SQL statement:

SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID

Want to know why the duplicates are there? Look at the SQL resultset, Hibernate does not hide these duplicates on the left side of the outer joined result but returns all the duplicates of the driving table. If you have 5 orders in the database, and each order has 3 line items, the resultset will be 15 rows. The Java result list of these queries will have 15 elements, all of type Order. Only 5 Order instances will be created by Hibernate, but duplicates of the SQL resultset are preserved as duplicate references to these 5 instances. If you do not understand this last sentence, you need to read up on Java and the difference between an instance on the Java heap and a reference to such an instance.

(Why a left outer join? If you'd have an additional order with no line items, the result set would be 16 rows with NULL filling up the right side, where the line item data is for other order. You want orders even if they don't have line items, right? If not, use an inner join fetch in your HQL).

Hibernate does not filter out these duplicate references by default. Some people (not you) actually want this. How can you filter them out?

Like this:

Collection result = new LinkedHashSet( session.create*(...).list() );

In addition to what is mentioned by Eran, another way to get the behavior you want, is to set the result transformer:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

try

@Fetch (FetchMode.SELECT) 

for example

@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Fetch (FetchMode.SELECT)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;

}


Do not use List and ArrayList but Set and HashSet.

@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public Set<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

Using Java 8 and Streams I add in my utility method this return statment:

return results.stream().distinct().collect(Collectors.toList());

Streams remove duplicate very fast. I use annotation in my Entity class like this:

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;

I think is bether in my app to use session in method where I need data from data base. Closse session when I done. Ofcourse set my Entity class to use leasy fetch type. I go to refactor.