前言

如果有两个对象,分别时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条件查询

总结

对于出现的异常,不要担心害怕,最好是打断点调试,找到异常出现的位置。一旦找到抛出异常的位置,就能够根据代码逻辑有效地规避异常了。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐