4

Update

I'd like to note that @sainr's answer Converting Hibernate proxy to real entity object does solve the problem. But the issue behind the scene is my SiteEntity having a final modifier of it's setControllerEntity and getControllerEntity, which I didn't raise in my question. And I apologize.

Remove the final modifier. Then Hibernate can initialize the proxy objects just fine.

The explanation can be found in another answer on Stack Overflow.


I have three entities as following

@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;
}

After I found the device entity, I try to get the controllerEntity directly from it.

final DeviceEntity deviceEntity1 = deviceRepository.findOne(1L);
System.out.println(deviceEntity1.getSiteEntity().getControllerEntity().getId());

But it results a java.lang.NullPointerException which is caused by the null controllerEntity in the siteEntity.

Also, even if I tried to use siteRepositoy to fetch the siteEntity again. the controllerEntity of it is still null.

After I removed the fetch = FetchType.LAZY from both the DeviceEntity and SiteEntity, NPE doesn't happen anymore.

But it seems odd and doesn't make sense. Can I use FetchType.LAZY while expecting hibernate fetch the correct value?

Thanks.

Chiu
  • 350
  • 3
  • 14

2 Answers2

2

Hibernate work with primitive types sometimes not very well. Try to replace

private long id

to

private Long id

For primary keys in Hibernate it is better to use wrapper classes instead of primitive types.

Anton Tupy
  • 890
  • 5
  • 16
  • Thanks! I do find that in recent study on how hibernate handling null value of primitive type – Chiu Dec 27 '17 at 07:02
2

To give you an access to the field declared with FetchType.LAZY, Hibernate constructs a proxy with CGLIB. Consequently, when you're calling a getter for such field (in your case, getSiteEntity() or getControllerEntity()), you're not accessing the field value directly -- instead, the call is passed to the proxy object of Hibernate. In turn, Hibernate tries to load the actual value from the data store and in order to do this, it would require an active Hibernate session to access the DB. Most likely, in your case, the Hibernate session is already closed and such lazy load fails, giving you an effectively null value of the field.

There are basically two ways to solve this:

  1. Use FetchType.EAGER that would load all field values along with the holding object DeviceEntity
  2. Transform a proxy object into a real object (check Converting Hibernate proxy to real entity object) and access it in a regular way

Think about it, whether you really need a lazy load in your case. If you are not storing plenty of heavy objects in child fields to load them on demand, probably switching to FetchType.EAGER will be the easiest way.

Hope that helps.

Vladimir Salin
  • 2,248
  • 2
  • 33
  • 44
  • 1
    Thanks. It doesn't contain heavy object actually. I'll switch to EAGER instead. However, if there's a need for FetchType.LAZY, such as heavy object or ManyToMany, OneToMany mapping, is there anyway to prevent hibernate session to be closed or "transform a proxy object into a real object" is the way to go? Thanks – Chiu Dec 27 '17 at 07:12
  • 1
    @Chiu you can try using within `TransactionTemplate` (or mark a method where you're loading the holder object and accessing lazy children as `@Transactional`) – Vladimir Salin Dec 27 '17 at 07:25
  • 1
    I'd like to note that "Converting Hibernate proxy to real entity object" does solve the issue. But the issue behind the scene is my `SiteEntity` having a `final` modifier of it's `setControllerEntity` and `getControllerEntity`. Remove the `final` modifier. Then Hibernate can initialize the proxy object just fine. – Chiu Jan 03 '18 at 05:39