11

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();
3
  • are you sure you have transactionManager set in spring context and tx:annotation-driven defined? Commented Dec 2, 2012 at 16:27
  • yeah everything works ok everywhere else ... Commented Dec 2, 2012 at 16:34
  • can you post stack trace here? we will check if transaction handling code is there Commented Dec 2, 2012 at 16:49

4 Answers 4

12

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.

Sign up to request clarification or add additional context in comments.

12 Comments

yeah I thought the session is still open though thats the issue as the method is transactional and uses dao which has session injected into it.
By the time your lazily-associated-set is copied to be available for the caller of getFavorites() method, the session would be already closed, as the scope of the transaction ends at the end of getFavorites() method.
I don't understand at all. "scope of the transaction ends at the end of getFavorites()" thats when i want it to end, I have called user.getFavourties while session is open, it will load from db the data and populate a hashset ? (well obviously it doesn't and I am wrong, I soemtimes wish I never bothered with hibernate)
Please see the update where I tried to explain what exactly happens.
I can just do "Set<Foo> favourites = new HashSet<Foo>(user.getFavourites());"
|
9

There are two solutions.

  1. Don't use lazy load.

    Set lazy=false in XML or Set @OneToMany(fetch = FetchType.EAGER) In annotation.

  2. 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

2 Comments

This article discusses these proposed solutions: vladmihalcea.com/…
OpenInView is a bad solution as a first before any other available. There are lot more ways to fix this that we should use before this anti-pattern
2

Yes, the object should be accessed in the transactional context otherwise the operation will throw a LazyInitializationException.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.