Hibernate Eager Fetch gets Too Many Results

Working on a bug today, there was a list of items with a set of sub items coming back from the database via Hibernate. Something like this in pseudocode:

@Entity
@Table(name = "PARENT", schema = "USER")
public class Parent implements Serializable {

private Long id;
private String name;
private List<Child> childList = new ArrayList<Child>();

@Id
@SequenceGenerator
@Column(name="PARENT_ID")
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

@Id
@Column
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@OneToMany(mappedBy = "parent",fetch = FetchType.EAGER)
@Cascade(CascadeType.ALL)
public List<Child> getChildList() {
return childList;
}

public void setChildList(List<Child> childList) {
this.childList = childList;
}
}

@Entity
@Table(name = "CHILD", schema = "USER")
public class Child implements Serializable {

private Long id;
private String name;
private Parent parent;

@Id
@SequenceGenerator
@Column(name="CHILD_ID")
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

@Id
@Column
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@ManyToOne
@JoinColumn(name = "PARENT_ID")
public Parent getParent() {
return parent;
}

public void setParent(Parent parent) {
this.parent = parent;
}
}

@Repository
public class ParentDao implements SomeGenericDao {

public List<JasperReportTemplate> getAllActiveTemplates(Long firmId) {
final Criteria criteria = getCurrentSession()
       .createCriteria(getModelClass())
       .(DO SOME CRITERIA ETC>);
return criteria.list();
}
}

Here are your data sets for PARENT(parent_id, name):

  • (1,”parent1″)
  • (2,”parent2″)

And your data sets for CHILD(child_id,parent_id(foreign key),name)

  • (1,1,”child1″)
  • (2,1,”child2″)
  • (3,2,”child3″)
  • (4,2,”child4″)

When you try to make a list of *just* PARENT with this configuration you end up getting:

  1. parent1
  2. parent1
  3. parent2
  4. parent2

But what you want is this:

  1. parent1
  2. parent2

It’s because of that EAGER fetch — it forces everything to be in ONE BIG QUERY with all the tables so you get a Cartesian result. What you really want is just unique PARENT values.

Solutions

Removing “EAGER”

First, if you remove the EAGER, you won’t be loading any CHILD data. This could be problematic because the CHILD data won’t be available without making another data call to hydrate those objects. Depends on what you want.

Returning a Set instead of a List from the DAO

Another solution is something like this in the DAO in your java, that will return a set object that automatically filters the unique objects:

 Set = new LinkedHashSet();
 set.addAll(criteria.list());
 return set;

Supposably this is a workaround. it does work though.

Setting @Fetch

yet another solution, and one that I use, is to just set @Fetch on the child property:

 @OneToMany(mappedBy = "parent",fetch = FetchType.EAGER)
 @Cascade(CascadeType.ALL)
 @Fetch(FetchMode.SUBSELECT)
 public List<Child> getChildList() {
 return childList;
 }

I’ve also had success with using FetchMode.SELECT. This forces Hibernate to do a subquery on the child instead of one massive query — and you get all the data back.

Comments are closed.