【问题标题】:Kotlin - Data class entity throws StackOverflowErrorKotlin - 数据类实体抛出 StackOverflowError
【发布时间】:2018-08-02 06:16:38
【问题描述】:

我尝试将 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
    }   
}

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

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

在执行期间,第一个权限总是正确地添加到 appUser,但是添加第二个权限会产生 StackOverflowError 和堆栈跟踪

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

如果我将这些类设为非数据,它会正常工作。我可能可以通过覆盖 hashcode 和 equals 方法来解决这个问题,但是我有很多实体,所以我真的不想这样做。

【问题讨论】:

    标签: spring-boot kotlin


    【解决方案1】:

    AppUserUserAuthority 之间存在循环依赖关系。处理hashCode时需要排除一个来打破循环依赖。

    您可以通过将导致循环依赖的属性移动到数据类主体来修复它,这样这些属性就不会在自动生成的字段上使用。在这种情况下,它会将authorities 移动到AppUser 正文:

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

    【讨论】:

    • 不幸的是,它不起作用。可能是因为数据类不是由 lombok(由于 @Data 注释)仅由 kotlin 本身管理的。
    • 更新了我的答案以避免使用来自 lombok 的 @EqualsAndHashCode
    • 从技术上讲,这是一个正确的答案,但是正如我在问题中所说的那样,我确实有很多实体,其中许多实体具有循环依赖关系。所以我正在寻找某种注释或一些库,它们可以让我绕过覆盖每个实体的等于和哈希码。
    • 我的错,根据数据类文档,如果您可以在类主体中声明一个属性,它将不会在自动生成的函数中使用。也许您可以尝试将 val authorities 移动到 AppUser 正文。
    • 它正在工作,但是必须说明这个解决方案也有它的缺点。您将需要一个辅助构造函数或其他一些解决方案来初始化您从主构造函数中提取的参数,它在某种程度上是多余的,但冗余度较低,然后一遍又一遍地覆盖等于和哈希码。然而,最大的失望是您不再可以使用带有提取参数的复制方法。复制方法只能更改数据类主构造函数中的参数。
    猜你喜欢
    • 2015-10-28
    • 2012-04-29
    • 1970-01-01
    • 2019-02-05
    • 2013-01-12
    • 2018-07-10
    • 2023-03-10
    • 1970-01-01
    • 2020-03-06
    相关资源
    最近更新 更多