0

I am having this issue with JPA in my application. I have a parent entity Artist with one-to-one/many etch relations to other entities which I have all set to be lazily fetched. I fetch these entities using join queries. This all seems to work fine but sometimes i get a LazyInitializationException. I am using Stateless EJB's with JPA in the backend and Spring MVC in the web layer. Here is the method:

public Artist getArtistWithChildren(long id, boolean isFetchReviews, boolean isFetchRequests, boolean isFetchGigs, boolean isFetchVenues) {
    StringBuilder sql = new StringBuilder();
    sql.append("select a from Artist a join fetch a.members mrs");
    if(isFetchReviews) {
        sql.append(" left join a.reviews rvs");
    } if(isFetchRequests) {
        sql.append(" left join a.requests rqs");
    } if(isFetchGigs) {
        sql.append(" left join a.gigs gs");
    } if(isFetchVenues) {
        sql.append(" left join a.venues vs");
    }

    sql.append(" where a.id=:id");

    TypedQuery<Artist> query = em.createQuery(sql.toString(), Artist.class);
    query.setParameter("id", id);
    query.setMaxResults(1);
    List<Artist> resultList = query.getResultList();
    return resultList.get(0);
}

And here is the entity class Artist

@Entity
@Table(name="ARTIST")
public class Artist extends DescribingEntity {

private static final long serialVersionUID = -7264327449601568983L;

@ManyToMany(mappedBy="artists", targetEntity=Member.class, fetch=FetchType.LAZY, cascade={MERGE, REFRESH})
private List<Member> members;

@OneToMany(mappedBy="artist", targetEntity=VenueReview.class, fetch=FetchType.LAZY, cascade={MERGE, REFRESH, REMOVE})
private List<VenueReview> reviews;

@OneToMany(mappedBy="artist", targetEntity=Gig.class, fetch=FetchType.LAZY, cascade={MERGE, REFRESH, REMOVE})
private List<Gig> gigs;

@OneToMany(mappedBy="artist", targetEntity=GigRequest.class, fetch=FetchType.LAZY, cascade={MERGE, REFRESH, REMOVE})
private List<GigRequest> requests;

@ManyToMany(cascade={MERGE, REFRESH}, fetch=FetchType.LAZY)
@JoinTable(name="VENUE_ARTIST_REL", 
    joinColumns=@JoinColumn(name="ARTIST_ID", referencedColumnName="ARTIST_ID"), 
    inverseJoinColumns=@JoinColumn(name="VENUE_ID", referencedColumnName="VENUE_ID"))
private List<Venue> venues;

getters and setters...

I then go into debug mode to find out what went wrong and as I am stepping through the method it returns no error and no exception is thrown. Could it be that the collection is returned too soon so that all data from the DB has not had the time to populate correctly? I am a newbie when it comes to JPA so please let me know what I did wrong. Here is an example. I am using left joins here since I dont HAVE to get the results, but I doo need an instantiated collection.

I have tested the same thing using Arquillian and no errors, I also have similar methods for other entities where the scenario is the same, simply running it causes the error while step-through debugging don't.

Using Hibernate.initialize(Object o) on the child collections works fine but to my knowledge it is not a good thing to do since I then have to make a DB query for each child (correct me if I'm wrong).

I added a pastebin link to the stack trace

Stack Trace

Johan
  • 221
  • 3
  • 13

2 Answers2

1

In General, LazyInitializationException happens when you're lazy loading on a collection and trying to access that collection with out having a Session that encloses the context (method essentially) in which you're trying to access the lazy collection. It would be much more helpful, if you can post your error stack trace to get an exact resolution to your problem

Shriram
  • 74
  • 7
  • I added the stack trace as a link. – Johan Jan 17 '17 at 21:00
  • 1
    Without a Session, not without Transaction. E.g. detached entities – Marius K. Jan 17 '17 at 21:20
  • My bad, yes without a Session. Thanks for correcting me. – Shriram Jan 18 '17 at 02:56
  • 1
    I am able to see the following message: "could not initialize proxy - no Session". As I mentioned earlier, this problem is because you're trying to access the lazy loaded collection, but the Session on which the collection was loaded is already closed. Also, I can see from the stack trace, that you are using Spring as your application framework. If such is the case, try using @Transactional annotation on the method where you're accessing the lazy loaded collection. – Shriram Jan 18 '17 at 03:01
  • Adding configuration as separate comment since previous comment was too long. If you're using annotation configuration in spring, you need to use @EnableTransactionManagement annotation on your JPA configuration class. If you're using XML based config., you need and you need a bean of type org.springframework.transaction.PlatformTransactionManager defined. – Shriram Jan 18 '17 at 03:06
  • Thanks for the help. I am using spring mvc in the Web layer while the backend is using ejb with jpa. Could this explain why the arquillian tests pass while the application crash when loaded into a jsp? Maybe i should just go spring all the way. – Johan Jan 18 '17 at 18:04
  • 1
    Not really. If i understood your problem correctly, you have JSP view in to which you're passing the object obtained from your EJB layer and that is the point when your lazy collection is accessed. If yes, I'd say, try to access that in an early stage(a method which is bound within you Session's context), iterate through your collection, and set it into a different object (a DTO perhaps) that your ModelAndView returns to the JSP view. Word of caution: if you're planning to use OSIV, you have the risk of facing hibernate's N+1 problem which could increase view load time. – Shriram Jan 19 '17 at 21:25
  • Ahh, stupid me! I have used DTO's (for other reasons) in other controllers where it worked and I wasn't even thinking of that. That should also explain why the integration tests work while i get an error in the web layer. Thank you for you help! – Johan Jan 24 '17 at 21:44
1

Here is the cause explain:

The problem

A common issue in a typical (web-)application is the rendering of the view, after the main logic of the action has been completed, and therefore, the Hibernate Session has already been closed and the database transaction has ended. If you access detached objects that have been loaded in the Session inside your JSP (or any other view rendering mechanism), you might hit an unloaded collection or a proxy that isn't initialized. The exception you get is: LazyInitializationException: Session has been closed (or a very similar message). Of course, this is to be expected, after all you already ended your unit of work.

And a solution is call Open Session in View pattern. with spring based application, you can use org.springframework.orm.hibernate5.support.OpenSessionInViewInterceptor or org.springframework.orm.hibernate5.support.OpenSessionInViewFilter, but you cann't use both in one project indeed.

But for some reasion, it is treated as an antipattern:

** Reference: **

Open Session in View

Community
  • 1
  • 1
Liping Huang
  • 3,794
  • 3
  • 23
  • 39