Tips of using JPA, Hibernate and Spring Boot 3 Native
When building native Java apps using Spring Boot 3, if Spring JPA and Hibernate are used, here are some tips.
Hibernate Enhance plugin
Hibernate Enhance plugin can enhance JPA entity classes in the build time.
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
<enableDirtyTracking>true</enableDirtyTracking>
<enableAssociationManagement>true</enableAssociationManagement>
<enableExtendedEnhancement>false</enableExtendedEnhancement>
</configuration>
<executions>
<execution>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
Postgres support
To support Postgres in native images, extra configurations need to be provided to GraalVM.
This is done by implementing RuntimeHintsRegistrar
to provide reflection hints.
import org.hibernate.dialect.PostgreSQLDialect;
import org.postgresql.util.PGInterval;
import org.postgresql.util.PGobject;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
public class DatabaseRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection()
.registerType(PostgreSQLDialect.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)
.registerType(PGobject.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)
.registerType(PGInterval.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS);
}
}
Entities with @MappedSuperclass
When a JPA entity extends from a parent class with @MappedSuperclass
annotation, an error occurs when running the native image. In the example below, the entity io.vividcode.happyride.addressservice.domain.Address
extends from a parent class which defines the id
field. However, from the error message, we can see that Hibernate cannot find the id
field.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory': Entity 'io.vividcode.happyride.addressservice.domain.Address' has no identifier (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)
To fix this issue, parent classes also need to be configured with reflection support. In the code below, EntityWithGeneratedId
is configured to enable reflection for all declared fields.
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
public class DatabaseRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection()
.registerType(AbstractEntity.class, MemberCategory.DECLARED_FIELDS)
.registerType(
BaseEntityWithGeneratedId.class, MemberCategory.DECLARED_FIELDS)
.registerType(
EntityWithGeneratedId.class, MemberCategory.DECLARED_FIELDS);
}
}