Spring 数据:如何在从父关系中删除子关系时自动删除子关系

Spring Data: How to automatically remove a child relation when it is removed from the parent

我有一个 User 实体,它在 @OneToOne 关系中拥有一个 Character 实体。但是我希望他 Character 记录在与 User 实体分离后立即被删除。

这是我的 User.kt 实体 class:

// User.kt

@Entity
class User(
    @Id
    var id: String,
    var email: String,
    @OneToOne(cascade = [CascadeType.ALL], orphanRemoval = true)
    var character: Character?,
    var isAdmin: Boolean
) { // ... }

这是我为测试此行为而编写的单元测试:

// UserRepositoryTest.kt

@Test
fun `should remove orphan character entity when being removed from user entity`() {
    val user = UserTestTemplate.testUser()
    val character = CharacterTestTemplate.testCharacter()

    user.character = character
    userRepository.save(user)

    user.character = null
    userRepository.save(user)

    val actual = userRepository.findById(user.id).orElse(null)

    assertThat(actual).isNotNull()
    assertThat(actual.character).isNull()

    val savedCharacter = characterRepository.findById(character.id)
    assertThat(savedCharacter.get()).isNull() // fails
}

我添加了 CascadeType.ALLorphanRemoval = true 选项,因为这是我读到的唯一与我的请求相关的内容。

我在单元测试中所做的是创建一个用户和角色实例。然后将角色实例添加到用户并通过 UserRepository 保存用户。感谢 CascadeType.ALL 角色实例将自动保存。现在,当 从用户那里删除 角色时,我想反过来做同样的事情。然而,正如您在单元测试的最后一行中看到的那样,这并不像预期的那样工作

需要注意两点:

  • 事务后写模式
  • 一级缓存
@Test
fun `should remove orphan character entity  entity`() {
    val user = UserTestTemplate.testUser()
    val character = CharacterTestTemplate.testCharacter()

    user.character = character
    userRepository.save(user)

    user.character = null

    //use saveAndFlush here to force immediate DB update
    //otherwise may be deferred until transactional method returns
    userRepository.saveAndFlush(user)

    //clear the persistence context to ensure you will be reading from 
    //the database rather than first level cache
    //entityManager is injected to test via @PersistenceContext annotation
    entityManager.clear();

    //now you are guaranteed a db read reflecting all flushed updates
    val actual = userRepository.findById(user.id).orElse(null)

    assertThat(actual).isNotNull()
    assertThat(actual.character).isNull()

    val savedCharacter = characterRepository.findById(character.id)
    assertThat(savedCharacter.get()).isNull() // fails
}