Hi Folks,
I’m upgrading a Kotlin Based Spring Boot Service from 3.1 → 3.2.5 which bumps hibernate from 6.2.5.FINAL to 6.4.4.FINAL. We leverage the hibernate-enhance-maven-plugin
plugin in order to support parent side OneToOne lazy loading. We are also leveraging the all-open
plugin to make our Entity classes open. However, upon upgrading we noticed that the enhancement plugin has changed how it works and ends up breaking other lazy entity relationships.
For example, given the below entities, both the errorMetadata
and batch
associations are defined to use FetchType.LAZY
. However if a lazily entity relationship is accessed by one of its val
properties it does not trigger a database fetch, but when using a var
it does.
After some investigation between hibernate versions, the cause seems to be this change which appears to not add the read interceptor for final fields and was added in Hibernate 6.4.1 based on the tags. Kotlin vals look to be compiled as Final
For example here is the decompiled Java Code post all-open
and hibernate-enhance
in Hibernate 6.4.1. We can see that a field defined as val in Kotlin does not have any of the interceptor code and thus does not make a database call when accessing lazily via a Hibernate Proxy
And if we look at the enhanced code in Hibernate 6.2.5 we can see that the read interceptor is still added for a field that was defined as a val in Kotlin. This in term does trigger a database fetch as expected
Was wondering if there a way around this behaviour change? It does work if we change vals to vars, but that is not ideal as we want immutability of the field within our Kotlin code. It would be better if we can get the same behaviour as under 6.2.5.FINAL.
val activity = activityRepository.findById(1).get();
activity.batch?.idempotencyKey // Does not trigger a fetch to the database and returns null.
activity.batch?.otherField // DOES trigger a fetch to the database
@Entity
class Activity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
var id: Long? = null,
@OneToOne(mappedBy = "activity", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var errorMetadata: ErrorMetadata? = null,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "batch_id")
var batch: Batch? = null,
)
@Entity
class Batch(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
var id: Long? = null,
@Column(nullable = false)
val idempotencyKey: String,
@Column(nullable = false)
var otherField: String,
)
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>jpa</plugin>
<plugin>spring</plugin>
<plugin>all-open</plugin>
</compilerPlugins>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<pluginOptions>
<option>all-open:annotation=jakarta.persistence.Entity</option>
<option>all-open:annotation=jakarta.persistence.Embedded</option>
<option>all-open:annotation=jakarta.persistence.MappedSuperclass</option>
</pluginOptions>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
<plugins>
Thanks!