Основная проблема заключается в том, что Spring Data Mongo не может обработать первичный конструктор в классе Parent, когда он видит свойство Child и попадает в циклический цикл, следовательно, Stackoverflow.Тем не менее, у Jackson Mapper нет проблем с сериализацией и десериализацией!
Я открыл проблему Spring Data MongoDB JIRA DATA MONGO 2299 , где мне предложили Решение 1. Однако это не так.действительно Kotlin idiomatic.
Я надеюсь, что MongoDB имеет более нативное решение из коробки, так как Джексон может хорошо обрабатывать циклические ссылки.
Тем временем я немного исследовал время и нашел еще две альтернативы.Все 3 решения работают для сериализации и десериализации с обеими платформами: Spring Data MongoDB и Jackson Mapper.
Решение 1
Плюсы: очень лаконично
Минусы: не совсем идиоматически Kotlin (var id вместо val id, openкласс Parent)
class Child {
// In order to workaround the StackOverflow problem we lazy initialise the property.
@DBRef(lazy = true)
@JsonIgnore
lateinit var parent: Parent
}
// However, laziness requires Spring Data Mongo framework to subclass our Parent, hence we have to delcare it open
open class Parent(var id: String, val child: Child) {
init { child.parent = this }
}
Решение 2
Плюсы: Защищенный первичный конструктор, используемый для всех свойств, которые могут обрабатывать обе платформы
class Child {
@DBRef
@JsonBackReference
lateinit var parent: Parent
}
// Primary constructor called by MongoDB & Jackson for de-serialisation
class Parent protected constructor(val id: String) {
// Secondary constructor called by us
constructor(id: String, child: Child): this(id) {
this.child = child
this.child.parent = this
}
// We need @JsonManagedReference/@JsonBackReference on the child.
// Ohterwise we get `UninitializedPropertyAccessException`if Jackson de-serialize it and we try to access the parent property on the child
@JsonManagedReference
lateinit var child: Child
}
Решение 3
Плюсы: Полный контроль над конструкторами
Минусы: Подробно
class Child {
@DBRef
@JsonIgnore
lateinit var parent: Parent
}
class Parent {
val id: String
lateinit var child: Child
// Called by MongoDB
@PersistenceConstructor
protected constructor(id: String) {
this.id = id
}
// Called by Jackson Mapper & us
constructor(id: String, child: Child) {
this.id = id
this.child = child
this.child.parent = this
}
}
Надеюсь, это поможет кому-то с подобными проблемами.