Kotlin-数据类实体抛出StackOverflowError

pok*_*zok 2 kotlin spring-boot

我尝试将kotlin(1.2.21版)与spring-boot(1.5.9.RELEASE)结合使用。我在使用带有@Entity批注的数据类时遇到了问题。我的问题实体看起来像这样:

@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
    }   
}
Run Code Online (Sandbox Code Playgroud)

如您所见,AppUser和UserAuthority之间具有@OneToMany关系。现在,我尝试添加一些这样的权限:

authoritiesCollection.forEach { appUser.addRole(UserAuthority(appUser, Authority.valueOf(it))) }
Run Code Online (Sandbox Code Playgroud)

在执行期间,始终将第一权限正确地添加到appUser,但是添加第二权限会产生带有stacktrace的StackOverflowError

java.lang.StackOverflowError
at security.usermanagement.AppUser.hashCode(AppUser.kt)
at security.usermanagement.UserAuthority.hashCode(UserAuthority.kt)
Run Code Online (Sandbox Code Playgroud)

如果我使这些类成为非数据类,它将正常工作。我可能可以通过重写哈希码和equals方法来解决此问题,但是我有很多实体,所以我确实不是。

pab*_*obu 6

AppUser和之间有循环依赖关系UserAuthority。在处理hashCode时,需要排除一个以打破循环依赖关系。

您可以通过将导致循环依赖的属性移动到数据类主体来解决此问题,这样就不会在自动生成的字段上使用这些属性。在这种情况下,它将authoritiesAppUser身体移动:

@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)
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 我的错,根据数据类文档,如果您可以在类主体中声明一个属性,它将不会在自动生成的函数中使用。也许您可以尝试将 `val authority` 移到 AppUser 主体中。 (2认同)
  • 它正在工作,但是必须指出,该解决方案也有其缺点。您将需要一个辅助构造函数或其他一些解决方案来初始化您从主构造函数中提取的参数,它在某种程度上是冗余的,但不那么冗余,然后一遍又一遍地覆盖 equals 和 hashcode。然而,最大的缺点是您不能再对提取的参数使用复制方法。在复制方法中只能更改数据类的主构造函数中的参数。 (2认同)