使用@ManyToMany

时间:2019-01-05 23:05:29

标签: hibernate spring-boot jpa kotlin h2

尝试解决了类似问题几个小时后,我仍然无法解决问题:

我正在构建一个Sprint引导应用程序(在Kotlin中),该应用程序使用JPA(同样也处于休眠状态)和H2(不认为这是相关的)。我想对类User和Achievement之间的多对多关系进行建模(这样一个用户可以拥有多个成就,而一个成就可以由多个用户实现)。这是模型类:

@Entity
data class User(

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        val id: Int = 0,

        @Column(nullable = false)
        val name: String,

        @ManyToMany(cascade = [ CascadeType.PERSIST, CascadeType.MERGE ])
        val achievements: MutableSet<Achievement> = HashSet()

)
@Entity
data class Achievement(

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        val id: Int = 0,

        val key: String,

        @ManyToMany(mappedBy = "achievements")
        val users: MutableSet<User> = HashSet()

)

当我看到以下日志时,该模型似乎运行良好:

Hibernate: create table achievement (id integer not null, key varchar(255), priority integer not null, category_id integer, primary key (id))
Hibernate: create table user (id integer not null, name varchar(255) not null, primary key (id))
Hibernate: create table user_achievements (users_id integer not null, achievements_id integer not null, primary key (users_id, achievements_id))

然后我将数据预先填充在@Service @Transactional中,如下所示:

val achievement = Achievement(key = "SOME_ACHIEVEMENT_KEY")
achievementRepository.save(achievement)

val user = User(name = "John Doe")
userRepository.save(user)

并添加一个关系:

val foundAchievement = achievementRepository.findById(achievementId)
val foundUser = userRepository.findById(userId)

foundAchievement.ifPresent { achievement ->
    foundUser.ifPresent { user ->
        user.achievements.add(achievement)
        userRepository.save(user)
    }
}

如果我现在尝试通过在以下URL之一上执行GET来访问数据:

http://localhost:8080/achievements/2/users
http://localhost:8080/users/3/achievements

我收到一个java.lang.StackOverflowError,日志显示我休眠了一次又一次地查询成就和用户(无休止的循环直到堆栈溢出)。

这是我的问题:

  1. 您知道为什么会出现此错误吗?
  2. 我的模型正确吗?
  3. 将用户连接到成就上的方式是正确的还是我需要将用户添加到成就中?用户是否也需要(甚至将整个内容保存在AchievementRepository中)?

1 个答案:

答案 0 :(得分:2)

好的,问题是双向关系以及Kotlin自动生成的equals()和hashCode()的组合。

由于我使用的是Kotlin数据类,因此它会为主要构造函数中定义的每个属性自动生成hashCode()和equals()。我从主要构造函数中删除了@ManyToMany注释的属性,但这个问题消失了:

<RecyclerView
    ...
    app:spanCount="4"
    app:layoutManager="android.support.v7.widget.GridLayoutManager"
    />
androidx.recyclerview.widget.GridLayoutManager