jpa criteria query duplicate values in fetched list

comandante N picture comandante N · Apr 2, 2012 · Viewed 12.1k times · Source

I'm observing what I think is an unexpected behaviour in JPA 2 when fetching a list attribute with a criteria query.

My query is as follows (an extract of it):

CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<MainObject> c = b.createQuery(MainObject.class);
Root<MainObject> root = c.from(MainObject.class);
Join<MainObject, FirstFetch> firstFetch = (Join<MainObject, FirstFetch>) root.fetch(MainObject_.firstFetch);
firstFetch.fetch(FirstFetch_.secondFetch); //secondFetch is a list
c.select(root).distinct(true);

(So let's say I'm fetching a list as a property of the property of an object.)

The thing is when the query returns multiple results, secondFetch values are duplicated as many times as rows are returned. Each firstFetch should have just one secondFetch but has n instead. The only particularity i see in this case is all MainObjects happen to have the same FirstFetch instance. So my guess is the join is being crossed, which is normal, but then JPA fails to assign its secondFetch object to each one of the firstFetchs.

Mappings shouldn't be too special, the're more or less like this

@Entity
@Table(name="mainobject")
public class MainObject{
   //...
   private FirstFetch firstFetch;

   @ManyToOne(fetch=FetchType.LAZY)
   @JoinColumn(name="mainObject_column")
   public FirstFetch getFirstFetch() {
    return firstFetch;
   }
}

and

@Entity
@Table(name="firstFetch")
public class FirstFetch{
   //...
   private List<SecondFetch> secondFetch;

   @OneToMany(mappedBy="secondFetch")
   public List<SecondFetch> getSecondFetch() {
      return secondFetch;
   }
}

& finally

@Entity
@Table(name="secondFetch")
public class SecondFetch {
    //....
    private FirstFetch firstFetch; //bidirectional

     @ManyToOne
     @JoinColumn(name="column")
     public FirstFetch getFirstFetch() {
        return firstFetch;
     }
}

I've been looking for some sort of distinct sentence to apply to the fetch but there's none (would have been a 'patch' anyway...)

If i change

List<SecondFetch>

for

Set<SecondFetch>

i'll get the expected result thanks to Sets' Keys, so I do feel this is kind of a misbehaviour in JPA's lists.

I'm not an expert, though, so i could perfectlly be making some mistake in the mappings or query. Any feeback is very welcome to help clear this out. Thanks.

Answer

Jens picture Jens · Apr 19, 2012

I had the exact same problem though I was using JPA criteria API to do the query.

After some research I found a solution which you already mentioned (but was not available, since your not using criteria API): Using distinct.

With JPA criteria it would look like this:

CriteriaQuery<FirstFetch> query = cb.createQuery(FirstFetch.class);
Root<AbschnittC> root = query.from(FirstFetch.class);
root.fetch(FirstFetch_.secondFetch, JoinType.LEFT);
query.distinct(true); 

Without using query.distinct(true); the resultset was multiplied with the amount of objects in the secondFetch list.

Hibernate does have something like DISTINCT_ROOT_ENTITY which sound more adequate than just setting a query distinct. But I have not further investigated this. I am also using Hibernate as the JPA provider. Maybe setting the query distinct in JPA ends up using the same code as Hibernates DISTINCT_ROOT_ENTITY would?