Here's the basic mapping:
Client {
@OneToMany(mappedBy="client",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private Set<Group> groups = new HashSet<Group>();
}
Group {
@ManyToOne (cascade=CascadeType.ALL)
private Client client = new Client();
}
The problem I'm having is that when I query against Client, I'm getting a full client for each associated group. My queries are pretty simple and I've tried both criteria and HQL. Here's a sample criteria query:
Criteria crit = getSession().createCriteria(getPersistentClass());
crit.add(Restrictions.like("name", name);
crit.add(Restrictions.eq("state", state);
return crit.list();
What the heck am I doing wrong?
Modify your mapping of Client
as below,
Client {
@OneToMany(mappedBy="client",cascade=CascadeType.ALL, fetch=FetchType.LAZY)
private Set<Group> groups = new HashSet<Group>();
}
From the API docs,
You have the ability to either eagerly or lazily fetch associated entities. The fetch parameter can be set to FetchType.LAZY or FetchType.EAGER. EAGER will try to use an outer join select to retrieve the associated object, while LAZY will only trigger a select when the associated object is accessed for the first time. @OneToMany and @ManyToMany associations are defaulted to LAZY and @OneToOne and @ManyToOne are defaulted to EAGER.
Read the API docs here for more details.
This is because of the fetch type eager. Hibernate is putting the groups in the same query, using joins. This multiplies the clients.
Join fetching of collections is usually not a good idea. It has several side effect. You just found one.
- If you want to avoid lazy loading, just turn lazy off (I think it's the annotation
@LazyCollection(FALSE)
)
- If you don't care about the side effects: the multiplied clients you got from the query are still the same instances in memory. You could reduce them to a list of distinct instances.
- HQL does actually not take this option into account. You need to
join fetch
them explicitly.