JPA-failed to lazily initialize a collection of role, could not initialize proxy - no Session
·
前言
如果有两个对象,分别时OneSide和ManySide,它们满足一对多的关系,一个OneSide对应多个ManySide。通常我们在JPA中这么定义它们的关系:
@Entity
public class OneSide {
@OneToMany(mappedBy = "oneSide")
private Set<ManySide> manySides;
}
@Entity
public class ManySide {
@ManyToOne
@JoinColumn
private OneSide oneSide;
}
为了定义双向关系,需要在关系的拥有方(ManySide)添加@JoinColumn添加注解生成外键,同时在关系的被拥有方使用mappedBy属性关联另一边。
数据加载方式
不同注解获取数据的方式有两种:
- LAZY:懒加载,查询之后,没有数据,后续需要再查询一次给字段赋值,才有数据(最好不要调用字段的get方法)。
- EAGER:急加载,查询之后,字段有数据。
下面看看几个常用注解的数据获取方式:
- @Basic:EAGER
- @ElementCollection:Lazy
- @OneToMany:Lazy
- @ManyToOne:EAGER
- @OneToOne:EAGER
- @ManyToMany:Lazy
可以看出,集合字段一般是懒加载,单值字段一般是急加载的。
出现这个问题的原因
打断点调试发现,Set<T>接口的默认实现是PersistentSet,这个对象的toString()方法会调用read()方法,而read()初始化会判断Session是否存在,不存在的话,会抛出这个异常。下面是初始化的代码片段:
if ( session == null ) {
if ( allowLoadOutsideTransaction ) {
tempSession = openTemporarySessionForLoading();
}
else {
throwLazyInitializationException( "could not initialize proxy - no Session" );
}
}
else if ( !session.isOpenOrWaitingForAutoClose() ) {
if ( allowLoadOutsideTransaction ) {
tempSession = openTemporarySessionForLoading();
}
else {
throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
}
}
else if ( !session.isConnected() ) {
if ( allowLoadOutsideTransaction ) {
tempSession = openTemporarySessionForLoading();
}
else {
throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
}
}
所以,如果Session的状态不对,调用对象的toString()会抛出这个异常。
注意事项
根据上面的原因,要做到一下几点才不会抛出异常:
- 不在控制台打印对象本身
- 不调用懒加载的字段
关于JPA查询的更多信息,请参考JPA条件查询。
总结
对于出现的异常,不要担心害怕,最好是打断点调试,找到异常出现的位置。一旦找到抛出异常的位置,就能够根据代码逻辑有效地规避异常了。
更多推荐
所有评论(0)