You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When I upgraded my project from Spring Boot 3.3.6 to 3.4.0, it somehow causes Kotlin JPA entity objects to become detached, causing the exception jakarta.persistence.EntityExistsException: detached entity passed to persist.
I tried to narrow the problem down and found that it happens with the following combination of components:
Spring Boot 3.4.0 (which uses hibernate-core:6.6.2.Final) (but works fine with <= 3.3.6, which uses hibernate-core:6.5.3.Final)
@jakarta.persistence.Version annotation. When this is removed from a field from the Entity, the problem disappears.
Kotlin entities. It works fine when using Java entites with about the same code, but translated to Java.
I tried it with Spring Boot 3.4.0 using a downgraded hibernate-core from 6.6.2.Final to 6.5.3.Final, which causes the problem to dissapear. So it is related to Hibernate. But because Kotlin is also part of the problem and because it doesn't happen with plain Java, I thought the place to report this is in this Spring Data JPA project.
This is the failing Kotlin code when used with Spring Boot 3.4.0:
@Entity
class ProductK(
var name: String,
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0
)
@Entity
class ProductTagK(
@ManyToOne
var product: ProductK,
var tag: String,
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0
) {
// Removing this annotation removes the problem
@Version
private var updated: LocalDateTime = LocalDateTime.now()
}
/**
* Kotlin client code with Kotlin Entity.
* Fails with: Exception in thread "main" jakarta.persistence.EntityExistsException: detached entity passed to persist: demo.ProductTagK
*/
@Component
class DetachedEntityProblemK(
em: EntityManager,
transactionManager: PlatformTransactionManager
) : AbstractDetachedEntityProblemK(em, transactionManager) {
fun run() {
val query = "select p from ProductK p where p.name = 'Car'"
runInCommitTx { em ->
val p = em.createQuery(query, ProductK::class.java).resultList.firstOrNull()
if (p == null) {
println("Creating new product")
em.persist(ProductK("Car"))
} else {
println("Existing product found")
}
}
runInRollbackTx { em ->
val p = em.createQuery(query, ProductK::class.java)
.resultList.first()
val t = ProductTagK(p, "vehicle")
// This line fails
em.persist(t)
}
}
}
fun main() {
val context = runApplication<DemoApplication>()
context.getBean(DetachedEntityProblemK::class.java).run()
context.close()
}
It fails with:
Exception in thread "main" jakarta.persistence.EntityExistsException: detached entity passed to persist: demo.ProductTagK
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:126)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:167)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:173)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:767)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:745)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:320)
at jdk.proxy2/jdk.proxy2.$Proxy92.persist(Unknown Source)
at demo.DetachedEntityProblemJK.lambda$run$1(DetachedEntityProblemJK.java:37)
at demo.AbstractDetachedEntityProblemJ.runInRollbackTx(AbstractDetachedEntityProblemJ.java:26)
at demo.DetachedEntityProblemJK.run(DetachedEntityProblemJK.java:31)
at demo.DetachedEntityProblemJK.main(DetachedEntityProblemJK.java:44)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: demo.ProductTagK
at org.hibernate.event.internal.DefaultPersistEventListener.persist(DefaultPersistEventListener.java:90)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:79)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:761)
... 9 more
But about equivalent Java entities work fine:
@Entity
public class ProductJ {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public ProductJ() {}
public ProductJ(String name) {
this.name = name;
}
}
@Entity
public class ProductTagJ {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private ProductJ product;
private String tag;
@Version
private LocalDateTime updated = LocalDateTime.now();
protected ProductTagJ() {}
public ProductTagJ(ProductJ product, String tag) {
this.product = product;
this.tag = tag;
}
}
build.gradle.kts:
plugins {
java
kotlin("jvm") version "1.9.25"
kotlin("plugin.spring") version "1.9.25"
// Change this to 3.3.6 to remove the problem
id("org.springframework.boot") version "3.4.0"
// id("org.springframework.boot") version "3.3.6"
id("io.spring.dependency-management") version "1.1.6"
kotlin("plugin.jpa") version "1.9.25"
}
group = "demo"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
// Downgrading hibernate-core also removes the problem
// constraints {
// implementation("org.hibernate.orm:hibernate-core") {
// version {
// strictly("6.5.3.Final")
// }
// }
// }
// implementation("org.hibernate.orm:hibernate-core:6.5.3.Final")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.jetbrains.kotlin:kotlin-reflect")
runtimeOnly("com.h2database:h2")
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
allOpen {
annotation("jakarta.persistence.Entity")
}
Attached is the complete project code. It contains the following example runs:
DetachedEntityProblemJ: uses Java client code with Java entities. Runs fine.
DetachedEntityProblemJK: uses Java client code with Kotlin entities. Fails.
DetachedEntityProblemK: uses Kotlin client code with Kotlin entities. Fails.
DetachedEntityProblemKJ: uses Kotlin client code with Java entities. Runs fine.
This is a hibernate issue. Hibernate now requires to call merge if you provide a value for a generated Id or version. Please reach out to the Hibernate team if you have further questions or concerns.
When I upgraded my project from Spring Boot 3.3.6 to 3.4.0, it somehow causes Kotlin JPA entity objects to become detached, causing the exception
jakarta.persistence.EntityExistsException: detached entity passed to persist
.I tried to narrow the problem down and found that it happens with the following combination of components:
hibernate-core:6.6.2.Final
) (but works fine with <= 3.3.6, which useshibernate-core:6.5.3.Final
)@jakarta.persistence.Version
annotation. When this is removed from a field from the Entity, the problem disappears.I tried it with Spring Boot 3.4.0 using a downgraded hibernate-core from 6.6.2.Final to 6.5.3.Final, which causes the problem to dissapear. So it is related to Hibernate. But because Kotlin is also part of the problem and because it doesn't happen with plain Java, I thought the place to report this is in this Spring Data JPA project.
This is the failing Kotlin code when used with Spring Boot 3.4.0:
It fails with:
But about equivalent Java entities work fine:
build.gradle.kts:
Attached is the complete project code. It contains the following example runs:
demo.zip
The text was updated successfully, but these errors were encountered: