Johan
Johan

Reputation: 231

Join fetch in JPA sometimes causes LazyInitializationException

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

Upvotes: 0

Views: 1460

Answers (2)

Liping Huang
Liping Huang

Reputation: 4476

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

Upvotes: 1

Shriram
Shriram

Reputation: 74

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

Upvotes: 1

Related Questions