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
我找到了问题的解决方案:
- 将实体注释为不可变
- 使版本和 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
}
更新到 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
我找到了问题的解决方案:
- 将实体注释为不可变
- 使版本和 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
}