NullPointerException 发生在 FetchType.LAZY withi hibernate 和 spring boot
NullPointerException happens with FetchType.LAZY withi hibernate and spring boot
更新
我想指出@sainr 的回答 Converting Hibernate proxy to real entity object 确实解决了问题。但幕后的问题是我的 SiteEntity
有一个 final
修饰符 setControllerEntity
和 getControllerEntity
,我没有在我的问题中提出。我很抱歉。
删除 final
修饰符。然后 Hibernate 可以很好地初始化代理对象。
可以在 Stack Overflow 上的另一个 中找到解释。
我有以下三个实体
@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
,这是由 siteEntity
中的 null
controllerEntity
引起的。
此外,即使我尝试使用 siteRepositoy
再次获取 siteEntity。它的 controllerEntity
仍然是 null
.
在我从 DeviceEntity 和 SiteEntity 中删除 fetch = FetchType.LAZY
后,NPE 不再发生。
但这似乎很奇怪而且没有意义。我可以在期望休眠获取正确值的同时使用 FetchType.LAZY
吗?
谢谢。
Hibernate 有时不能很好地处理原始类型。尝试替换
private long id
至
private Long id
对于 Hibernate 中的主键,最好使用包装器 类 而不是原始类型。
为了让您能够访问用 FetchType.LAZY
声明的字段,Hibernate 使用 CGLIB 构造了一个代理。因此,当您为此类字段调用 getter 时(在您的情况下为 getSiteEntity()
或 getControllerEntity()
),您不会直接访问字段值——相反,调用是传递给 Hibernate 的代理对象。反过来,Hibernate 会尝试从数据存储中加载实际值,为此,它需要一个活动的 Hibernate 会话来访问数据库。在您的情况下,Hibernate 会话很可能已经关闭并且此类延迟加载失败,从而为您提供了有效的 null
字段值。
基本上有两种方法可以解决这个问题:
- 使用
FetchType.EAGER
加载所有字段值以及持有对象 DeviceEntity
- 将代理对象转换为真实对象(检查Converting Hibernate proxy to real entity object)并以常规方式访问它
想一想,您的情况是否真的需要延迟加载。如果您没有在子字段中存储大量重对象以按需加载它们,可能切换到 FetchType.EAGER
将是最简单的方法。
希望对您有所帮助。
更新
我想指出@sainr 的回答 Converting Hibernate proxy to real entity object 确实解决了问题。但幕后的问题是我的 SiteEntity
有一个 final
修饰符 setControllerEntity
和 getControllerEntity
,我没有在我的问题中提出。我很抱歉。
删除 final
修饰符。然后 Hibernate 可以很好地初始化代理对象。
可以在 Stack Overflow 上的另一个
我有以下三个实体
@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
,这是由 siteEntity
中的 null
controllerEntity
引起的。
此外,即使我尝试使用 siteRepositoy
再次获取 siteEntity。它的 controllerEntity
仍然是 null
.
在我从 DeviceEntity 和 SiteEntity 中删除 fetch = FetchType.LAZY
后,NPE 不再发生。
但这似乎很奇怪而且没有意义。我可以在期望休眠获取正确值的同时使用 FetchType.LAZY
吗?
谢谢。
Hibernate 有时不能很好地处理原始类型。尝试替换
private long id
至
private Long id
对于 Hibernate 中的主键,最好使用包装器 类 而不是原始类型。
为了让您能够访问用 FetchType.LAZY
声明的字段,Hibernate 使用 CGLIB 构造了一个代理。因此,当您为此类字段调用 getter 时(在您的情况下为 getSiteEntity()
或 getControllerEntity()
),您不会直接访问字段值——相反,调用是传递给 Hibernate 的代理对象。反过来,Hibernate 会尝试从数据存储中加载实际值,为此,它需要一个活动的 Hibernate 会话来访问数据库。在您的情况下,Hibernate 会话很可能已经关闭并且此类延迟加载失败,从而为您提供了有效的 null
字段值。
基本上有两种方法可以解决这个问题:
- 使用
FetchType.EAGER
加载所有字段值以及持有对象DeviceEntity
- 将代理对象转换为真实对象(检查Converting Hibernate proxy to real entity object)并以常规方式访问它
想一想,您的情况是否真的需要延迟加载。如果您没有在子字段中存储大量重对象以按需加载它们,可能切换到 FetchType.EAGER
将是最简单的方法。
希望对您有所帮助。