Tips of using JPA, Hibernate and Spring Boot 3 Native

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);
  }
}
© 2023 VividCode