Project environment:
Spring 3.4.3
Spring Data JPA 1.10.4
Hibernate 5.2.5
JPA2.1
Problem
- OneToOne properties not lazy loading, although specifying fetch=LAZY
- OneToOne properties can lazy loading, specifying fetch=LAZY, optional=false, but save new entity error
Define Entity Phone and PhoneDetails, they are have bidirectional OneToOne associations.
@Entity(name = "Phone")
public class Phone {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "number")
private String number;
@OneToOne(mappedBy = "phone", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
private PhoneDetails details;
public Phone() {
}
public Long getId() {
return id;
}
public String getNumber() {
return number;
}
public PhoneDetails getDetails() {
return details;
}
public void setDetails(PhoneDetails details) {
this.details = details;
}
}
@Entity
public class PhoneDetails {
@Id
private Long id;
private String provider;
private String technology;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "id")
private Phone phone;
public PhoneDetails() {
}
public String getProvider() {
return provider;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
}
Save a new phone with new details:
Phone phone = new Phone();
PhoneDetails details = new PhoneDetails();
phone.setDetails(details);
details.setPhone(phone);
phoneDao.save(phone);
- When setting association specifying “optional=true” (i.e. default value), above codes work OK. But phone’s details property can not lazy loading, although specify “fetch = FetchType.LAZY”. Other associations (e.g. ManyToOne, ManyToMany, OneToMany) lazy load normally.
- Runing above codes specifying “optional=false” in two entities, will throw exception like following:
org.springframework.orm.jpa.JpaSystemException: null id generated for:class PhoneDetails; nested exception is org.hibernate.id.IdentifierGenerationException: null id generated for:class PhoneDetails
Solution(for me using solution 3):
1.using hibernate bytecode enhance build time
- toOne(OneToOne, ManyToOne) properties annotation with (no_proxy, lazy,optional=true)
- no need to change save codes.
- need to setting build time hibernate enhance plugin
for maven:
<build>
<plugins>
[...]
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernateVersion}</version>
<executions>
<execution>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
<enableDirtyTracking>false</enableDirtyTracking>
< >false</enableAssociationManagement>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
[...]
</plugins>
</build>
if eclipse reporting error, add settings:
<pluginManagement>
<plugins>
[...]
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<versionRange>[1.0.0,)</versionRange>
<goals>
<goal>enhance</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute />
</action>
</pluginExecution>
</lifecycleMappingMetadata>
</configuration>
</plugin>
[...]
</plugins>
</pluginManagement>
notice:
- get one lazy property will trigger all lazy properties loading, because they are in one lazy group.
- ManyToMany propertis possiblly not loading(v5.25), save the entity caused to lose the association.
- enableDirtyTracking and enableAssociationManagement existing some issues in version 5.25?
2.using jpa bytecode enhance run time
reference hibernate document, in my test, not success.
3.split two steps for saving entity:
- save parent entity(phone) first,
- save child entity(phoneDetails) secondly.
Phone phone = new Phone();
PhoneDetails details = new PhoneDetails();
phoneDao.save(phone);
phone.setDetails(details);
details.setPhone(phone);
detailDao.save(details);
above codes work normally.
Conclution
From my test, hibernate bytecode enhance build time and runing time possiblly have certain issues, run time more servere, please care to use. Futhermore, bytecode enhance must add Hibernate Annotations in your domain entities, not JPA specification. Good news is we can avoid to use bytecode enhance, like solution 3, meanwhile eliminate Hibernate Annations and enhance plugin setting.