Cross fetch created on bi-directional One-to-many

2019-09-07 16:22发布

问题:

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?

回答1:

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.



回答2:

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.