Spring Data Rest Put 未在 SpringBoot 2 上使用不可变实体进行更新。1.x

Spring Data Rest Put not updating with immutable entity on SpringBoot 2.1.x

更新到 spring 启动启动器版本 2.1.5 后,put 请求不再使用不可变实体更新实体。

Put 请求在 spring boot starter 2.0.3 上有效,但在 2.1.4 和 2.1.5 上无效。然而,使用 Patch 方法效果很好。

因为我在 Kotlin 工作,所以我的代码是用 Kotlin 编写的,但我也可以用 Java 实体重现它:

鉴于实体:

@Entity
data class MyEntityKt(
     @Id
     @GeneratedValue
     val id: UUID? = null,
     @Version
     val version: Long = 0,
     val name: String = "",
     val description: String = ""
)
@Value
@Wither
@Entity
@RequiredArgsConstructor
public class MyEntityJava {

    public MyEntityJava() {
        id = null;
        version = 0;
        name = "";
        description = "";
    }

    @Id
    @GeneratedValue
    private final UUID id;
    @Version
    private final long version;
    private final String name;
    private final String description;
}

以及对应的两个RestRepositories:

@RepositoryRestResource(path = "kotlin")
interface MyEntityRepository : JpaRepository<MyEntityKt, UUID>

@RepositoryRestResource(path = "java")
interface ImmutableEntityRepository : JpaRepository<MyEntityJava, UUID>

在我的测试 (@SpringBootTest) 中,我尝试了以下操作:

@Test
fun testPut() {
    val myEntity = myEntityRepository.save(MyEntityKt(name = "TestName", description = "TestDescription"))
    val update = this.mockMvc.perform(
        put("/kotlin/${myEntity.id}")
            .accept(MediaType.APPLICATION_JSON_UTF8)
            .contentType(MediaType.APPLICATION_JSON_UTF8)
            .content(
                """
                {
                    "name": "TestNameModified",
                    "description": "TestDescriptionModified"
                }
            """.trimIndent()
            )
    )
    .andReturn().response.contentAsByteArray.let { objectMapper.readValue(it, MyEntityKt::class.java) }

    val updatedEntity = myEntityRepository.findById(myEntity.id!!).get()

    assertThat(updatedEntity.name).isEqualTo("TestNameModified")    
    assertThat(updatedEntity.description).isEqualTo("TestDescriptionModified")
}

另外我得到的回复是:

{
    "name" : "TestName",
    "description" : "TestDescription",
    "_links" : {
    "self" : {
        "href" : "http://localhost/kotlin/fd83f256-3515-41b0-b9c7-7bfe53a367f8"
    },
    "myEntityKt" : {
        "href" : "http://localhost/kotlin/fd83f256-3515-41b0-b9c7-7bfe53a367f8"
    }
}

我做了一些调查,org.springframework.data.rest.webmvc.json.DomainObjectReader.MergingPropertyHandler#doWithPersistentProperty 我猜它会负责创建我的实体的更新版本以持久保存,似乎它只是在访问器上设置值但实际上并没有创建更新的对象.

在 spring 引导版本 2.0.3 上,测试用例通过,在 2.1.5 上,但是实体没有更新,并且没有给出实体未更新的实际原因(例如,日志输出)更新。我的猜测是,这与实体是不可变的事实有关,但我不明白为什么这在以前有效。

编辑:我将测试用例上传到 Github

我找到了问题的解决方案:

  1. 将实体注释为不可变
  2. 使版本和 id 属性 可变
@Entity
@org.springframework.data.annotation.Immutable
data class MyEntityKt(
     @Id
     @GeneratedValue
     var id: UUID? = null,
     @Version
     var version: Long = 0,
     val name: String = "",
     val description: String = ""
)

Immutable 注释导致 json 响应我将成为正确更新的响应,但它使用我的更新值向数据库中插入了一个新条目。

因此,为了使其覆盖现有实体,我必须使 ID 和版本可变。

同样在 json 我发送更新实体我包含了用于后续更新的版本,如下所示:

{
    "name": "TestNameModified",
    "description": "TestDescriptionModified",
    "version": 0
}