Reputation: 47310
Foo looks has this in it :
@ManyToMany
private Set<User> favouritedBy;
while user has this:
@ManyToMany(mappedBy = "favouritedBy")
private Set<Foo> favourites = new HashSet<Foo>();
public Set<Foo> getFavourites() {
return favourite;
}
And fooService has this, with the lazyloaded collection being accessed while session is opened, via the tranactional method :
@Transactional(readOnly = true)
public Set<Foo> getFavourites(User user) {
user = dao.get(User.class, user.getId()); //the dao gets a session
Set<Foo> favourites = user.getFavourites();//but the session is not here and the exception is thrown?
return favourties;
}
EDIT This fixes it, without using criteria :
Set<Foo> favourites = new HashSet<Foo>(user.getFavourites());
and this fixes it with criteria
Session session = sessionFactory.getCurrentSession();
final Criteria crit = session.createCriteria(Foo.class);
crit.setFetchMode("favourites", FetchMode.JOIN);
crit.add(Property.forName("id").eq(id));
return (Foo) crit.uniqueResult();
Upvotes: 11
Views: 46583
Reputation:
If you use any of @...Many... relationships along with Fetch type "Lazy" and you're getting LazyInitializationException - that means you got OpenInView turned off, that is good.
To avoid both LazyInitializationException and turning OIV (that makes Hibernate session open longer than in most cases needed) - ensure you specified @Fetch(FetchMode.JOIN) on the issuing column.
Example: Before:
@ManyToMany(fetch = FetchType.LAZY)
private Set<Kek> keks;
After:
@Fetch(FetchMode.JOIN)
@ManyToMany(fetch = FetchType.LAZY)
private Set<Kek> keks;
This way you will force Join Fetch type that will (very simply talking) provide correct query with required linked entities Joined, not forcing you to use Eager fetch.
Upvotes: 0
Reputation: 21
Yes, the object should be accessed in the transactional context otherwise the operation will throw a LazyInitializationException
.
Upvotes: 2
Reputation: 3405
There are two solutions.
Don't use lazy load.
Set lazy=false
in XML or Set @OneToMany(fetch = FetchType.EAGER)
In annotation.
Use lazy load.
Set lazy=true
in XML or Set @OneToMany(fetch = FetchType.LAZY)
In annotation.
and add filter in your web.xml
<listener>
...
</listener>
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate4.support.OpenSessionInViewFilter
</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>mySessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
...
</servlet>
And <param-value>mySessionFactory</param-value>
is your sessionFacory bean name that defined in applicationContext.xml
Upvotes: 9
Reputation: 24134
The default FetchType
in a ManyToMany
is LAZY
and the hibernate documentation for working with lazy associations clearly calls out this kind of access as an error. You can interact with lazily associated objects only while the session is still open. That portion of the documentation also provides alternatives to access such lazily associated members of an object . We prefer to specify the fetch mode as JOIN
in the criteria used, in our applications
Edit:
Set<Foo> favourites = user.getFavourites();
The above statement doesn't actually return a set that contains all the Foo
objects. It is just a proxy. The actual Foo
objects are fetched only when the elements in the set are accessed like favorites.iterator()
etc., This operation is clearly happening outside your getFavorites()
method. But the @Transactional
annotation on the getFavorites()
method indicates that the session will be closed at the end of this method.
So, when methods are called on the favourites set, the session is already closed and hence the exception.
To address this, you should use a Criteria object to retrieve the user and specify the fetch type as JOIN
so that the Foo objects are populated in the User object returned.
Upvotes: 12