在JPA中加入fetch有时会导致LazyInitializationException

时间:2017-01-17 14:22:22

标签: hibernate spring-mvc ejb jpa-2.0 lazy-initialization

我在我的应用程序中遇到了JPA这个问题。我有一个父实体艺术家与其他实体的一对一/多个蚀刻关系,我已经设置为懒惰获取。我使用连接查询获取这些实体。这一切似乎工作正常,但有时我得到一个LazyInitializationException。我在后端使用无状态EJB和在Web层使用Spring MVC。这是方法:

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);
}

这是实体类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...

然后我进入调试模式以找出出错的地方,因为我正在逐步执行该方法,它不会返回任何错误,也不会抛出任何异常。可能是收集过早返回,以便数据库中的所有数据都没有时间正确填充?说到JPA,我是新手,所以请让我知道我做错了什么。这是一个例子。我在这里使用左连接,因为我没有得到结果,但我需要一个实例化的集合。

我使用Arquillian测试了相同的东西并且没有错误,我也有类似的方法用于场景相同的其他实体,简单地运行它会导致错误,而逐步调试则不会。

在子集合上使用Hibernate.initialize(Object o)工作正常,但据我所知,这不是一件好事,因为我必须为每个孩子进行数据库查询(如果我错了,请纠正我)。 / p>

我在堆栈跟踪

中添加了一个pastebin链接

Stack Trace

2 个答案:

答案 0 :(得分:1)

一般情况下,LazyInitializationException发生在你对一个集合进行延迟加载并尝试访问该集合时,没有一个Session包含你试图访问lazy集合的上下文(本质上是方法)。如果您可以发布错误堆栈跟踪以获得问题的精确解决方案,那将会更有帮助

答案 1 :(得分:1)

以下是原因解释:

  

问题

     

典型(web)应用程序中的一个常见问题是在操作的主要逻辑完成之后呈现视图,因此,Hibernate会话已经关闭并且数据库事务已经结束。如果访问已在JSP(或任何其他视图呈现机制)内的Session中加载的分离对象,则可能会遇到未加载的集合或未初始化的代理。您得到的异常是:LazyInitializationException:会话已关闭(或非常类似的消息)。当然,毕竟你已经结束了你的工作单元,这是可以预期的。

解决方案是调用在视图中打开会话模式。使用基于Spring的应用程序,您可以使用org.springframework.orm.hibernate5.support.OpenSessionInViewInterceptororg.springframework.orm.hibernate5.support.OpenSessionInViewFilter,但您不能在一个项目中同时使用它们。

但是对于某些原因,它被视为反模式:

**参考:**

Open Session in View