javajpakotlinstack-overflowbidirectional

StackOverflowError with JPA bidirectional references in Kotlin


I have data classes as following:

@Entity
@Table(name = "SECTIONS")
data class Section(

        @Id @GeneratedValue
        @Column(name = "ID")
        var id: Long = 0,

        @Column(name = "NAME")
        var name: String = "",

        @OneToMany(
                mappedBy = "section",
                fetch = FetchType.EAGER,
                cascade = arrayOf(CascadeType.ALL),
                orphanRemoval = true
        )
        var fields: MutableList<Field> = mutableListOf()
)

@Entity
@Table(name = "FIELDS")
data class Field(

        @Id @GeneratedValue
        @Column(name = "ID")
        var id: Long = 0,

        @Column(name = "NAME")
        var name: String = "",

        @ManyToOne
        @JoinColumn(name = "SECTION_ID")
        var section: Section? = null
)

As you can see, there is a bidirectional mapping between Section and Field. When I create a Section object, a Field object and I add the Field object to the list of fields in the Section object, it works fine. However, when I also set the Field's section reference to the Section object and then persist, I get a StackOverflowError:

@Test
fun testCascadeSaving() {
    val section = Section(name = "Section 1")
    val field = Field(name = "Field 1")

    section.fields.add(field)
    field.section = section

    val savedSection = sectionRepository.save(section)
    val savedField = savedSection.fields[0]

    // This causes an StackOverflowError
    val f = fieldRepository.findOne(savedField.id)
}

I have to comment the field.section = section line in order for the code above to work properly.

Any ideas why setting bidirectional relationship causes this error?


Solution

  • I've actually managed to solve the problem - all I've had to do was to override the toString() method in at least one of the entities. The implementations provided by Kotlin included calling each other's toString() methods recursively thus resulting in StackOverflowError.

    @Entity
    @Table(name = "FIELDS")
    data class Field(
    
            @Id @GeneratedValue
            @Column(name = "ID")
            var id: Long = 0,
    
            @Column(name = "NAME")
            var name: String = "",
    
            @ManyToOne
            @JoinColumn(name = "SECTION_ID")
            var section: Section? = null
    ) {
        override fun toString(): String {
            return "Field(id=$id, name=$name)"
        }
    }