Using FetchType.EAGER with @ElementCollection results in multiple entity copies returned via Criteria query

Description

If we specify FetchType.EAGER on an @ElementCollection:

@ElementCollection(fetch = FetchType.EAGER)
@Column(name = "emailAddress")
private Set<String> emailAddresses;

then request all persisted objects of this class via Criteria:

session.createCriteria(ReportSchedule.class).list();

The returned list contains multiple references to each ReportSchedule, one per email address.

Environment

MySQL

Activity

Show:
Greg Burcher
November 3, 2011, 3:57 PM

The work-around seems simple enough, but the problem is that we would need to add this call to setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) to every use of Criteria.list(). Otherwise, at any point in time, someone could change the mapping of an @ElementCollection to EAGER and suddenly the Criteria.list() would be "broken", returning multiple references to the same object.

This is a cumbersome convention to enforce in our entire codebase. We could handle by wrapping the hibernate Session or Criteria in our own class and adding the call to setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) always in our wrapper, but this is a significant design decision to correct what feels like an error in hibernate, even if you guys say it is not a bug.

I'm guessing since you have already published a FAQ on this issue, you are not likely to change this behavior?

John Thomas
August 30, 2012, 11:49 AM
Edited

I tend to agree that this is a bug. I also spent nearly a day trying to work out what was going on. I am using JPA2.0 annotation/objects with Hibernate 4.1.4. My example is as follows:

  • Employee entity with a ManyToOne relationship with a Schedule Entity. * The Employee entity also has a OneToMany relationship with a Qualification Entity.

  • The Schedule entity has an ElementCollection (java type List) of Embeddable type ScheduleEntry (e.g. start/stop time).

  • The ElementCollection is set for Eager loading.

Let's assume I have an employee record with four qualification entries and a Schedule with a single ScheduleEntry record. When I load the Employee object via EntityManager.findById, Hibernate will issue a single SQL SELECT joining the results, thus retrieving four rows. Since the ScheduleEntry table has no unique identifier, hibernate will populate the List in the Schedule object with 4 copies of the ScheduleEntry.

I fail to see any situation where this is desirable and in all situations it seems to be the wrong result. Changing between lazy and eager fetching should not result in a user being delivered an object with entirely different content, and in the latter case, the wrong content.

I also don't see why it is required. All of the information is available for hibernate to detect this situation, and when it exists, use two SQL queries. As far as I am aware, EAGER doesn't require a single SQL request be used, just that all fields should be populated during the find call.

I understand that using a Set will solve this, but that seems like a hack at the user level. And I can see situations where multiple copies of identical objects might be required.

EDIT:
I am using H2 v1.3.167, not MySQL

Steve Ebersole
September 4, 2012, 5:03 AM

As Christian said, not a bug. Use one of the solutions listed on the FAQ (not all of which say to use a Set!)

Kapil Koju
March 9, 2018, 2:01 PM

Unlink Criteria, HQL returns only single entity rather than multiple entities. So, HQL and Criteria behave differently when using FetchType.EAGER with @ElementCollection.

Steve Ebersole
March 9, 2018, 2:43 PM

Not true. HQL will also return the "duplicated" entities. This is even required by the JPA spec. The difference is that HQL and Hibernate's legacy Criteria API is that HQL disregards the eager on the initial query - you get a N+1. Do the same thing in HQL (here from ReportSchedule s join fetch s.emailAddresses) and you will get the same exact results.

For posterity... feel free to add comments here of course, but this will not be changing. We have even deprecated support for the legacy Criteria API, so it will doubly not change.

Assignee

Unassigned

Reporter

Greg Burcher

Fix versions

None

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Affects versions

Priority

Major
Configure