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 修饰符 setControllerEntitygetControllerEntity,我没有在我的问题中提出。我很抱歉。

删除 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 字段值。

基本上有两种方法可以解决这个问题:

  1. 使用 FetchType.EAGER 加载所有字段值以及持有对象 DeviceEntity
  2. 将代理对象转换为真实对象(检查Converting Hibernate proxy to real entity object)并以常规方式访问它

想一想,您的情况是否真的需要延迟加载。如果您没有在子字段中存储大量重对象以按需加载它们,可能切换到 FetchType.EAGER 将是最简单的方法。

希望对您有所帮助。