【发布时间】:2019-10-06 18:20:51
【问题描述】:
更新到 spring boot starter 版本 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 Boot 版本 2.0.3 上,测试用例通过,但在 2.1.5 上,实体没有更新,并且没有给出实体没有更新的实际原因(例如,日志输出)。我的猜测是,这与实体是不可变的这一事实有关,但我不明白为什么以前会这样。
编辑:我将测试用例上传到Github
【问题讨论】:
-
因为您从一级缓存中获得了相同的实体,而不是新的。在对
EntityManager执行findById调用clear之前注入EntityManager,然后执行findById以强制从数据库重新加载。此外,您的实体在 JPA 意义上也不是一成不变的,使用反射您仍然可以更改它们。你的 JPA 提供者可能有一些扩展,比如 hibernate 有@Immutable注释。 -
感谢您的建议,但这并不能解决问题,我编辑了问题,您现在可以看到我返回的 json 响应也显示了实体的旧状态。
标签: java spring spring-boot kotlin spring-data-rest