Issue
In the following code, how can hibernate change the value of the val
member id
? Even the reference to the object is a val
so I cannot see how a different object might be being substituted.
import jakarta.persistence.*
import java.util.UUID
@Entity
class Bar (val v: Int = 0) {
@Id @GeneratedValue @Column(columnDefinition="UUID default gen_random_uuid()")
val id = UUID.fromString("00000000-0000-0000-0000-000000000000")
}
fun main () {
val emf = HibernatePersistenceProvider().createContainerEntityManagerFactory(
MyPersistenceUnitInfo(), Properties()
)
emf.createEntityManager().let { em ->
em.transaction.begin()
val b = Bar(7)
println(b.id) // prints 00000000-0000-0000-0000-000000000000
em.persist(b)
println(b.id) // prints 82a7bc5a-f92d-4d7f-872a-3eb3ba7a5541
em.transaction.commit()
em.close()
}
}
I am indeed using the JPA plugin in my build.gradle.kts
as follows:
plugins {
kotlin("jvm") version "latest.release"
kotlin("plugin.serialization") version "latest.release"
id("com.google.cloud.tools.jib") version "latest.release"
id("com.google.cloud.artifactregistry.gradle-plugin") version "latest.release"
id("org.jetbrains.kotlin.plugin.jpa") version "latest.release"
application
}
But I am not using the "all open" plugin, if that matters.
Finally I'm still confused how val id
within a val b
references changes due the em.persist(b)
call as shown by the two println
s
Solution
If you disassemble the compiled class Bar
with javap you will see that the signature is:
public final class foo.Bar {
private final int v;
private final java.util.UUID id;
public foo.Bar(int);
public foo.Bar(int, int, kotlin.jvm.internal.DefaultConstructorMarker);
public final int getV();
public final java.util.UUID getId();
public foo.Bar();
}
... so your assumptions on the generated class are correct. But final
fields are not protected from modification with reflection, so Hibernate can change the value of the id
field anyhow...
If you set a breakpoint in the java.lang.reflect.Field.set(Object obj, Object value)
method and run your test with the debugger, you will see the call and the access path that Hibernate uses...
Answered By - Per Huss
Answer Checked By - Senaida (JavaFixing Volunteer)