org.hibernate.LazyInitializationException: could not initialize proxy - no Session, encore un fois
Asked Answered
F

4

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();
Fleer answered 2/12, 2012 at 16:23 Comment(3)
are you sure you have transactionManager set in spring context and tx:annotation-driven defined?Meristic
yeah everything works ok everywhere else ...Fleer
can you post stack trace here? we will check if transaction handling code is thereMeristic
A
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.

Ambo answered 2/12, 2012 at 16:27 Comment(12)
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.Fleer
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.Ambo
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)Fleer
Please see the update where I tried to explain what exactly happens.Ambo
@Transactional annotation guarantees that session is open until method ends, it you should be able to use lazy collections hereMeristic
@hoaz, true, but if you start accessing the collection / set outside this method, then the proxy that is actually returned by the user.getFavorites() method doesn't have a valid session to fetch them from the data store.Ambo
yeah, that is why i asked him to post stack traceMeristic
@Meristic this answer explains it thanks. Although it seems somewhat weird to me, I thought the lazyload would happen on the user.getFav() not on manipulating the actual collection itself.Fleer
I can just do "Set<Foo> favourites = new HashSet<Foo>(user.getFavourites());"Fleer
Forcing the set to be traversed within the session boundaries, nice!Ambo
still seems a bit ugly tbh, and wastes cycles, I will have a go at creating the crieria with fetch type join, but it looks complex too.Fleer
Thanks a lot! Your info helped me to figure out that the only missing thing at my Column definition was @Fetch(FetchMode.JOIN) above the @ManyToMany(fetch = FetchType.LAZY) @JoinTableTarrsus
C
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

Chucho answered 4/12, 2014 at 4:14 Comment(2)
This article discusses these proposed solutions: vladmihalcea.com/…Baileybailie
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-patternTarrsus
C
2

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

Character answered 21/11, 2013 at 17:1 Comment(0)
T
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.

Tarrsus answered 29/11, 2020 at 13:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.