使用hibernate和spring boot时,FetchType.LAZY会发生NullPointerException

问题描述 投票:3回答:2

更新

我想指出@ sainr的回答Converting Hibernate proxy to real entity object确实解决了这个问题。但是场景背后的问题是我的SiteEntity有一个final修饰符,它的setControllerEntitygetControllerEntity,我没有在我的问题中提出。我道歉

删除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.NullPointerExceptionnull中的controllerEntity siteEntity引起。

此外,即使我尝试使用siteRepositoy再次获取siteEntity。它的controllerEntity仍然是null

我从DeviceEntity和SiteEntity中删除了fetch = FetchType.LAZY后,NPE不再发生了。

但它似乎很奇怪,没有意义。我可以使用FetchType.LAZY,同时期望hibernate获取正确的值吗?

谢谢。

java hibernate spring-boot lazy-loading
2个回答
2
投票

为了让您访问使用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可能是最简单的方法。

希望有所帮助。


2
投票

Hibernate使用原始类型有时不太好。尝试更换

private long id

private Long id

对于Hibernate中的主键,最好使用包装类而不是基本类型。

© www.soinside.com 2019 - 2024. All rights reserved.