更新
我想指出@ sainr的回答Converting Hibernate proxy to real entity object确实解决了这个问题。但是场景背后的问题是我的SiteEntity
有一个final
修饰符,它的setControllerEntity
和getControllerEntity
,我没有在我的问题中提出。我道歉
删除final
修饰符。然后Hibernate可以很好地初始化代理对象。
可以在Stack Overflow上的另一个answer中找到解释。
我有三个实体如下
@Entity
@Table(name = "controller")
public class ControllerEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(nullable = false, updatable = false)
private long id;
}
@Entity
@Table(name = "site")
public class SiteEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(nullable = false)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "controller_id", nullable = false)
private ControllerEntity controllerEntity;
}
@Entity
@Table(name = "device")
public class DeviceEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(nullable = false)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "site_id", nullable = true)
private SiteEntity siteEntity;
}
在找到设备实体后,我尝试直接从中获取controllerEntity
。
final DeviceEntity deviceEntity1 = deviceRepository.findOne(1L);
System.out.println(deviceEntity1.getSiteEntity().getControllerEntity().getId());
但它导致java.lang.NullPointerException
由null
中的controllerEntity
siteEntity
引起。
此外,即使我尝试使用siteRepositoy
再次获取siteEntity。它的controllerEntity
仍然是null
。
我从DeviceEntity和SiteEntity中删除了fetch = FetchType.LAZY
后,NPE不再发生了。
但它似乎很奇怪,没有意义。我可以使用FetchType.LAZY
,同时期望hibernate获取正确的值吗?
谢谢。
为了让您访问使用FetchType.LAZY
声明的字段,Hibernate使用CGLIB构造代理。因此,当你为这样的字段调用getter时(在你的情况下,getSiteEntity()
或getControllerEntity()
),你不是直接访问字段值 - 而是将调用传递给Hibernate的代理对象。反过来,Hibernate尝试从数据存储加载实际值,为了做到这一点,它需要一个活动的Hibernate会话来访问数据库。最有可能的是,在您的情况下,Hibernate会话已经关闭,并且这种延迟加载失败,为您提供该字段的有效null
值。
基本上有两种解决方法:
FetchType.EAGER
将加载所有字段值以及保持对象DeviceEntity
想想看,你是否真的需要一个懒惰的负载。如果您没有在子场中存储大量重物以按需加载它们,那么切换到FetchType.EAGER
可能是最简单的方法。
希望有所帮助。
Hibernate使用原始类型有时不太好。尝试更换
private long id
至
private Long id
对于Hibernate中的主键,最好使用包装类而不是基本类型。