Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin - Data class entity throws StackOverflowError

I try to combine kotlin (version 1.2.21) with spring-boot (1.5.9.RELEASE). I have encountered a problem using data class with @Entity annotation. My problematic entities look like this:

@Entity
@Table(name = "APP_USER")
data class AppUser(

    @Column(name = "USERNAME", unique = true)
    private val username: String,

    @Column(name = "PASSWORD")
    private val password: String,

    @Column(name = "IS_ACTIVE")
    val isActive: Boolean,

    @Column(name = "REGISTRATION_DATE_TIME")
    val registrationDateTime: LocalDateTime = SystemTimeManager.getSystemDateTime(),

    @OneToMany(mappedBy = "appUser", cascade = [CascadeType.ALL], fetch = FetchType.EAGER)
    val authorities: MutableSet<UserAuthority> = mutableSetOf()
) : EntityBase(), UserDetails {


   internal fun addRole(authority: UserAuthority) {
      this.authorities.add(authority)
   }

}


@Entity
@Table(name = "USER_AUTHORITY")
data class UserAuthority(
    @ManyToOne
    @JoinColumn(name = "APP_USER_ID", nullable = false)
    val appUser: AppUser,

    @Column(name = "ROLE", length = 50, nullable = false)
    @Enumerated(value = EnumType.STRING)
    private val authority: Authority
) : EntityBase(), GrantedAuthority {

override fun getAuthority(): String {
    return authority.name
    }   
}

As you see, we have here @OneToMany relation between AppUser and UserAuthority. Now I try to add few authorities like this:

authoritiesCollection.forEach { appUser.addRole(UserAuthority(appUser, Authority.valueOf(it))) }

During execution first authority is always correctly added to appUser, however adding second authority produces StackOverflowError with a stacktrace

java.lang.StackOverflowError
at security.usermanagement.AppUser.hashCode(AppUser.kt)
at security.usermanagement.UserAuthority.hashCode(UserAuthority.kt)

If I make these class non data it works as it should. I probably can fix this by overriding hashcode and equals methods however I have many entities so I really rather not.

like image 579
pokemzok Avatar asked Feb 22 '18 11:02

pokemzok


People also ask

How do I stop StackOverflowError?

Increase Thread Stack Size (-Xss) Increasing the stack size can be useful, for example, when the program involves calling a large number of methods or using lots of local variables. This will set the thread's stack size to 4 mb which should prevent the JVM from throwing a java. lang. StackOverflowError .

What is a StackOverflowError?

A stack overflow is a type of buffer overflow error that occurs when a computer program tries to use more memory space in the call stack than has been allocated to that stack.

Which of the following expressions is most likely to cause a StackOverflowError?

The most common cause of StackOverFlowError is excessively deep or infinite recursion.


1 Answers

You have a circular dependency between AppUser and UserAuthority. You need to exclude one to break the circular dependency when processing the hashCode.

You can fix it by moving the properties that cause de circular dependency to the data class body, that way those properties won't be used on the autogenerated fields. In this case, it would be moving authorities to the AppUser body:

@Entity
@Table(name = "APP_USER")
data class AppUser(

        @Column(name = "USERNAME", unique = true)
        private val username: String,

        @Column(name = "PASSWORD")
        private val password: String,

        @Column(name = "IS_ACTIVE")
        val isActive: Boolean,

        @Column(name = "REGISTRATION_DATE_TIME")
        val registrationDateTime: LocalDateTime = SystemTimeManager.getSystemDateTime(),

) {
    @OneToMany(mappedBy = "appUser", cascade = [CascadeType.ALL], fetch = FetchType.EAGER)
    val authorities: MutableSet<String> = mutableSetOf()

    internal fun addRole(authority: String) {
        this.authorities.add(authority)
    }

}
like image 180
pablobu Avatar answered Sep 21 '22 04:09

pablobu