3

I have entity with two Lists of children entities. When calling EntityManager.find() it returns duplicates based on multiplication of two lists.

I'm using Spring, Jackson and JPA with Hibernate and SQL Server.

When testing with parent that has N elements of first and M of second child entity it always returns N*M elements of both entities.

For example below there are 3 tasks and 5 comments and JPA returns 15 for both lists. (5 copies of task list, and 3 copies of comment list)

Output from controller is:

Comments 15
Tasks 15

And the rest of the code is below.

controller.java

@RequestMapping(method = RequestMethod.GET)
public String listAll(Model model) {

    Goal goal = new Goal();

    goal = service.getGoalById(25);

    System.out.println("Comments " + goal.getComments().size());
    System.out.println("Tasks " + goal.getTasks().size());

    return "home";
}

service.java

@Transactional
public Goal getGoalById(int goalId) {
    Goal goal = em.find(Goal.class, goalId);

    return goal;
}

goal.java

@Entity
@Table(name = "goal")
public class Goal {

@Id
@GeneratedValue
private int id;

@JsonManagedReference
@OneToMany(mappedBy = "tasksGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private List<Task> projectTasks = new ArrayList<Task>();

@JsonManagedReference
@OneToMany(mappedBy = "commentsGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private List<Comment> goalComments = new ArrayList<Comment>();

...

}

task.java

@Entity
@Table(name="projectTask")
public class Task {

@Id
@GeneratedValue
private int id;

@JsonBackReference
@ManyToOne (fetch = FetchType.LAZY)
@JoinColumn(name = "task_goal_id")
private Goal tasksGoal;

...

}

comment.java

@Entity
@Table (name="goalComment")
public class Comment {

@Id
@GeneratedValue
private int id;

@JsonBackReference
@ManyToOne (fetch = FetchType.LAZY)
@JoinColumn(name = "goal_id")
private Goal commentsGoal;

...

}

persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="persistenceUnit"
    transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServer2008Dialect" />
        <property name="hibernate.hbm2ddl.auto" value="update" />
        <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
        <property name="hibernate.connection.charSet" value="UTF-8" />

        <!-- Hibernate prints SQL -->
        <property name="hibernate.show_sql" value="true" />

    </properties>
</persistence-unit>

  • Possible duplicate of [Hibernate Criteria returns children multiple times with FetchType.EAGER](http://stackoverflow.com/questions/1995080/hibernate-criteria-returns-children-multiple-times-with-fetchtype-eager) – Alan Hay May 04 '16 at 10:27
  • 1
    Marked as duplicate. Possible quick fixes. Either remove Eager fetch from the associations or change collections from List to Set. – Alan Hay May 04 '16 at 10:28
  • Thank you very much for quick fix, I used Set. But I still think this question is not duplicate. I'm not using HQL and somehow you would expect JPA to handle or inform you about this errors when using find method. I fully understand why this is happening but do not think that is easy to find answer. Overall thank you very much! :) – Veljko Rankovic May 04 '16 at 10:37
  • Error in the JPA provider, so raise a bug on it if not already one). It should never return dups for that. Presumably because internally it has issued some SQL that is inappropriate for the situation (which you can check). – Neil Stockton May 04 '16 at 14:52
  • Isn't this really a "multiple bags" fetch issue? [Multiple fetches with EAGER type in Hibernate with JPA](http://stackoverflow.com/questions/17566304/multiple-fetches-with-eager-type-in-hibernate-with-jpa) or [org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags](http://stackoverflow.com/questions/24675340/org-hibernate-loader-multiplebagfetchexception-cannot-simultaneously-fetch-mult) – K.Nicholas May 04 '16 at 18:25

2 Answers2

4

This is because of your fetch = FetchType.EAGER. he Hibernate tries to get everything in one shot, i.e. using one single query for each entry of element linked to a "master" object. Hibernate creates JOINs and tries to get all your collections with one query.

This problem can be successfully solved at a cost of N+1 query, if you add the @Fetch (FetchMode.SELECT) annotation to your collection.

If you really need FetchType.EAGER and don't want to replace your collection with Set you can use @Fetch (FetchMode.SELECT) annotation for your collection: @JsonManagedReference @OneToMany(mappedBy = "tasksGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) @Fetch (FetchMode.SELECT) private List projectTasks = new ArrayList();

@JsonManagedReference
@OneToMany(mappedBy = "commentsGoal", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@Fetch (FetchMode.SELECT)
private List<Comment> goalComments = new ArrayList<Comment>();
Tanvi B
  • 1,577
  • 11
  • 14
0
@Fetch (FetchMode.SELECT) 

can help you. it is the default fetching strategy. it enabled the lazy loading of all it’s related collections.

Arif Acar
  • 1,461
  • 2
  • 19
  • 33