在 JPA 中加入 fetch 有时会导致 LazyInitializationException
Join fetch in JPA sometimes causes LazyInitializationException
我在我的应用程序中遇到了 JPA 问题。我有一个父实体艺术家与其他实体的 one-to-one/many 蚀刻关系,我都设置为延迟获取。我使用连接查询获取这些实体。这一切似乎工作正常,但有时我会收到 LazyInitializationException。我在后端使用带有 JPA 的无状态 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);
}
这是实体class艺术家
@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...
然后我进入调试模式以找出哪里出了问题,当我逐步执行该方法时,它 returns 没有错误,也没有抛出异常。会不会是集合返回得太早,以至于数据库中的所有数据都没有时间正确填充?我是 JPA 的新手,所以请让我知道我做错了什么。这是一个例子。我在这里使用左连接,因为我不需要得到结果,但我确实需要一个实例化的集合。
我已经使用 Arquillian 测试了相同的东西并且没有错误,对于场景相同的其他实体我也有类似的方法,只是 运行 它会导致错误而逐步调试不会.
在子集合上使用 Hibernate.initialize(Object o)
效果很好,但据我所知这不是一件好事,因为我必须为每个子集合进行数据库查询(如果我错了请纠正我) .
我在堆栈跟踪中添加了一个 pastebin link
通常,当您延迟加载集合并尝试访问该集合而没有包含您尝试访问延迟集合的上下文(本质上是方法)的会话时,就会发生 LazyInitializationException。如果您可以 post 您的错误堆栈跟踪以准确解决您的问题,那将会更有帮助
原因解释如下:
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.
一个解决方案是调用 Open Session in View 模式。对于基于 spring 的应用程序,您可以使用 org.springframework.orm.hibernate5.support.OpenSessionInViewInterceptor
或 org.springframework.orm.hibernate5.support.OpenSessionInViewFilter
,但您确实不能在一个项目中同时使用两者。
但出于某种原因,它被视为反模式:
- The OpenSessionInView antipattern
- Why is Hibernate Open Session in View considered a bad practice?
** 参考:**
我在我的应用程序中遇到了 JPA 问题。我有一个父实体艺术家与其他实体的 one-to-one/many 蚀刻关系,我都设置为延迟获取。我使用连接查询获取这些实体。这一切似乎工作正常,但有时我会收到 LazyInitializationException。我在后端使用带有 JPA 的无状态 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);
}
这是实体class艺术家
@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...
然后我进入调试模式以找出哪里出了问题,当我逐步执行该方法时,它 returns 没有错误,也没有抛出异常。会不会是集合返回得太早,以至于数据库中的所有数据都没有时间正确填充?我是 JPA 的新手,所以请让我知道我做错了什么。这是一个例子。我在这里使用左连接,因为我不需要得到结果,但我确实需要一个实例化的集合。
我已经使用 Arquillian 测试了相同的东西并且没有错误,对于场景相同的其他实体我也有类似的方法,只是 运行 它会导致错误而逐步调试不会.
在子集合上使用 Hibernate.initialize(Object o)
效果很好,但据我所知这不是一件好事,因为我必须为每个子集合进行数据库查询(如果我错了请纠正我) .
我在堆栈跟踪中添加了一个 pastebin link
通常,当您延迟加载集合并尝试访问该集合而没有包含您尝试访问延迟集合的上下文(本质上是方法)的会话时,就会发生 LazyInitializationException。如果您可以 post 您的错误堆栈跟踪以准确解决您的问题,那将会更有帮助
原因解释如下:
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.
一个解决方案是调用 Open Session in View 模式。对于基于 spring 的应用程序,您可以使用 org.springframework.orm.hibernate5.support.OpenSessionInViewInterceptor
或 org.springframework.orm.hibernate5.support.OpenSessionInViewFilter
,但您确实不能在一个项目中同时使用两者。
但出于某种原因,它被视为反模式:
- The OpenSessionInView antipattern
- Why is Hibernate Open Session in View considered a bad practice?
** 参考:**