Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Kotlin: DB-Relationen in Spring Boot Data / Hibernate
RegularItem-Entity (analog dazu SpecialItem und RareItem):
Java:
import jakarta.persistence.*
@Entity
class RegularItem(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Int? = null,
var itemId: Int,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "action_id")
var action: Action? = null,
)
Und die Main:
Java:
@SpringBootApplication
class ManyToOneApplication(
val specialItemRepository: SpecialItemRepository,
val regularItemRepository: RegularItemRepository,
val rareItemRepository: RareItemRepository,
val actionRepository: ActionRepository,
): CommandLineRunner {
override fun run(vararg args: String?) {
kotlin.run {
val regularItem = RegularItem(itemId = 11111)
val rareItem = RareItem(itemId = 22222)
val specialItem = SpecialItem(itemId = 33333)
this.regularItemRepository.save(regularItem)
this.rareItemRepository.save(rareItem)
this.specialItemRepository.save(specialItem)
val action = Action(actionName = "PROCESS")
this.actionRepository.save(action)
val ac = this.actionRepository.findById(action.id!!)
ac.stream().forEach { println(it.regularItems.size) }
}
}
}
Ich hätte eigentlich erwartet, dass die gespeicherten Entities in die Action-Entity verbunden werden. Wenn ich diese allerdings über action.regularItems abfrage, wird diese Verknüpfung nicht hergestellt. Die size ist 0. Warum ist das so?
Ich habe gerade realisiert, dass mir noch die Zuweisung bei der Deklaration von Action fehlte:
Java:
@SpringBootApplication
class ManyToOneApplication(
val specialItemRepository: SpecialItemRepository,
val regularItemRepository: RegularItemRepository,
val rareItemRepository: RareItemRepository,
val actionRepository: ActionRepository,
): CommandLineRunner {
override fun run(vararg args: String?) {
kotlin.run {
val regularItem = RegularItem(itemId = 11111)
val rareItem = RareItem(itemId = 22222)
val specialItem = SpecialItem(itemId = 33333)
this.regularItemRepository.save(regularItem)
this.rareItemRepository.save(rareItem)
this.specialItemRepository.save(specialItem)
val action = Action(
actionName = "PROCESS",
regularItems = listOf(regularItem),
specialItems = listOf(specialItem),
rareItems = listOf(rareItem)
)
this.actionRepository.save(action)
val ac = this.actionRepository.findById(action.id!!)
ac.stream().forEach { println(it.regularItems.size) }
}
}
}
fun main(args: Array<String>) {
runApplication<ManyToOneApplication>(*args)
}
Nun fliegt das ganze hoch:
Code:
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.walk.many_to_one.many_to_one.model.RareItem
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:310) ~[spring-orm-6.1.8.jar:6.1.8]
Ich hatte es auch schon mit cascade = [CascadeType.PERSIST] probiert, leider ohne erfolg.
mittlerweile konnte ich die bisherigen Probleme lösen. Ich habe dazu nochmal die Dokumentation gelesen und verstehe, dass die ganzen item-Entitäten die "Owning-Side" der Beziehung sind. Ich wollte die Abfrage auf die jeweiligen Items über die Action durchführen und habe dementsprechend dann auch gemeint ich müsste die items samt der Action instanziieren und persistieren.
Der Code sieht nun so aus:
Java:
@SpringBootApplication
class ManyToOneApplication(
val specialItemRepository: SpecialItemRepository,
val regularItemRepository: RegularItemRepository,
val rareItemRepository: RareItemRepository,
val actionRepository: ActionRepository,
): CommandLineRunner {
override fun run(vararg args: String?) {
kotlin.run {
val action = Action(
actionName = "PROCESS"
)
this.actionRepository.save(action)
val regularItem = RegularItem(itemId = 11111, action = action)
val rareItem = RareItem(itemId = 22222, action = action)
val specialItem = SpecialItem(itemId = 33333, action = action)
this.regularItemRepository.save(regularItem)
this.rareItemRepository.save(rareItem)
this.specialItemRepository.save(specialItem)
}
}
}
fun main(args: Array<String>) {
runApplication<ManyToOneApplication>(*args)
}
Java:
@Entity
class Action(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var id: Int = 0,
var actionName: String,
@OneToMany(mappedBy = "action", cascade = [CascadeType.ALL], fetch = FetchType.LAZY, targetEntity = RareItem::class)
var rareItems: List<RareItem> = arrayListOf(),
@OneToMany(mappedBy = "action", cascade = [CascadeType.ALL], fetch = FetchType.LAZY, targetEntity = RareItem::class)
var specialItems: List<SpecialItem> = arrayListOf(),
@OneToMany(mappedBy = "action", cascade = [CascadeType.ALL], fetch = FetchType.LAZY, targetEntity = RareItem::class)
var regularItems: List<RegularItem> = arrayListOf(),
)
Java:
@Entity
class RegularItem(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var id: Int = 0,
var itemId: Int,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "action_id")
var action: Action? = null,
)
Kleine korrektur -> targetEntity = RareItem::class ist nicht notwendig bei Verwendung einer mutableList.
Nachdem das Beispiel lauffähig ist frage ich mich, wie ich mit einem Performance-Problem umgehe, wenn ich bspw. sehr viele Actions habe, die bspw. einer weiteren Tabelle hinzugefügt werden und Actions wiederrum sehr große Tabellen der jeweiligen Item-Kategorie.
Ich wollte dazu eine Tabelle anlegen, die eine flache Liste aller itemIds und eine Referenz auf die actionId / actionName hält erstellen, weiß aber noch nicht so recht wie ich das am besten machen könnte. Ratschläge hierzu?
Ich sehe einen Kotlin Code zum ersten Mal, aber wenn ich richtig verstanden habe, dann bringst du in deiner Logik etwas durcheinander. Egal wie viele Items du hast, sie können sich alle auf eine Action Entity beziehen, weil sie durch eine 1:n Beziehung mit ihr verbunden sind.
Du brauchst nur die Getter/Setter-Methoden für deine Items und dann kannst du alle deine Items bei einer Action Entity speichern.