Hibernate 5.1.10 - lazy loading doesn't work


#1

Hello,

I am working with Wildfly 11 which uses hibernate 5.1.10

Lazy loading doesn’t work for me, it always makes eager loading. I am doing following:

@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "device")
@LazyToOne(LazyToOneOption.NO_PROXY)
public DeviceRegistrationEntity getDeviceRegistration() {
	return deviceRegistration;
}

In Wildfly 8.2.0 (which user more older version of hibernate) I used org.hibernate.bytecode.internal.javassist.FieldHandled and org.hibernate.bytecode.internal.javassist.FieldHandler but these classes not exist in hibernate 5.1.10

Can you advice please how to switch on lazy-loading mode ?

Thanks,
Arkady.


#2

Lazy loading works except for the parent side of a @OneToOne association. This is because Hibernate has no other way of knowing whether to assign a null or a Proxy to this variable. More details you can find in this article.

Now, to fix it, you have two options:

  1. You can activate lazy loading bytecode enhancement
  2. Or, you can just remove the parent side and use the client side with @MapsId as explained in this article. This way, you will find that you don’t really need the parent side since the child shares the same id with the parent so you can easily fetch the child by knowing the parent id.

#3

I tried to use @Maps but it didn’t help. Still makes eager loading. Here is my code:

@SuppressWarnings("serial")
@Entity
@Table(name = "DEVICE")
public class DeviceEntity implements Serializable{
       private DeviceRegistrationEntity deviceRegistration;
       private Integer id;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "ID")
	public Integer getId() {
		return id;
	}
    
	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "device")
	public DeviceRegistrationEntity getDeviceRegistration() {
		return deviceRegistration;
	}
}

@SuppressWarnings("serial")
@Entity
@Table(name = "DEVICE_REGISTRATION")
public class DeviceRegistrationEntity implements Serializable{
     private DeviceEntity device;

	@OneToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "DEVICE_ID", insertable = false, updatable = false)
	@MapsId
	public DeviceEntity getDevice() {
		return device;
	}

	public void setDevice(DeviceEntity device) {
		this.device = device;
	}
}

During loading DeviceEntity, it loads also the child, i.e. DeviceRegistrationEntity

Any help is appreciated.

Thanks in advance.


#4

If you read that article carefully, you will see that by using @MapsId, you don’t really need to use the @OneToOne with mappedBy on the parent side.

Just adding @MapsId to your client-side does not solve the eager on the parent side, but it makes the mappedBy side redundant.

So, remove this:

@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "device")
public DeviceRegistrationEntity getDeviceRegistration() {
	return deviceRegistration;
}

Since you can now fetch the DeviceRegistrationEntity like this:

DeviceEntity deviceEntity = ...
DeviceRegistrationEntity dre = entityManager.find(DeviceRegistrationEntity.class, deviceEntity.getId());

If you really need to have the mappedBy @OneToOne, then you will find another solution in my previous answer.


#5

I can’t remove reference to child object on parent side.

I solved the problem by adding “optional = false” to all my OneToOne relations.
Works fine, but I don’t understand why does it work. Can you explain please ?

Anyway, I am very appreciate your help.

Thanks,
Arkady.


#6

The optional trick does not work on every version of Hibernate, so it might break if you upgrade.


#7

Suppose I take this solution and remove reference to “deviceRegistration” from “DeviceEntity”. You wrote:

DeviceEntity deviceEntity = …
DeviceRegistrationEntity dre = entityManager.find(DeviceRegistrationEntity.class, deviceEntity.getId());

As far as I know, “find” function of EntityManager works by primary key. In my case primary key of DeviceRegistrationEntity is not the same as DeviceEntity, it has it’s own ID field, it also has

   private Integer id;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
public Integer getId() {
	return id;
}

So how to fetch it knowing DeviceEntity ? By named query ?


#8

A one-to-one association without sharing the PK is a design smell. According to database table relationship, a one-to-one association always shares the PK.

In your case, unless you change your design, you’ll have to stick to using a separate PK which doubles the amount of memory that requires indexing since you need an index for the PK and another one for the FK.


#9

Vlad, thank you very-very much for quick and detailed answers. Now everything is clear me with one-to-one association.

Do you have an article about lazy loading in one-to-many association ? We also have problem there and I want to know how to do it correctly.

Thanks in advance.


#10

For OneToMany, the lazy loading is straightforward so you don’t have to worry about it. Just use LAZY and you are ok.