【发布时间】:2018-01-19 02:03:22
【问题描述】:
由于版本 2.5.7 Spring Data REST 无法正确执行 PUT 请求来更新具有关联资源的资源。与按预期工作的 PATCH 请求不同!
例如,Person 与 Addres 具有多对一关联。如果我们使用 SDR v.2.5.6 (Spring Boot v.1.4.3) 执行 PUT 请求,那么一切正常。但是如果我们切换到 2.5.7 版本(即 Spring Boot v.1.4.4),我们会得到一个错误:
无法构造地址的实例:没有字符串参数构造函数/工厂方法可以从字符串值反序列化
其他类型的关联也会发生同样的情况,例如一对多(单向和双向) - 请参阅我的 example application 代码和测试。
自 1.4.4 以来的 所有 版本的 Spring Boot 中都存在此问题,包括最新的稳定 1.5.6 版本以及最新的 2.0.0-SNAPSHOT 版本!
要解决这种情况,我们只需切换到 SDR v.2.5.6 (Spring Boot v.1.4.3)。
我已经准备了 Postman 请求集合来帮助您解决这个问题:SDR PUT Issue
2017 年 8 月 14 日更新
我找到了如何避免错误Can not construct instance of Address: no String-argument constructor/factory method to deserialize from String value。
由于我在这个项目中使用Lombok,
只需告诉 Lombok 使用 @ConstructorProperties 注释在
generated constructors。
所以我在“lombok.config”文件中设置了lombok.anyConstructor.suppressConstructorProperties=true,错误就消失了。
不幸的是,发现了一个新问题 - PUT 请求根本不会更新关联的对象!
下面的例子证明了这一点。当我们试图通过将他的地址从addresses/1(初始值)更改为addresses/2 来更新Person 时,它保持不变:addresses/1!与之前的问题一样,这个问题存在于自 1.4.4 以来的 所有 版本的 Spring Boot(SDR - 从 v.2.5.7 开始)。
我调试了我的项目,发现问题的原因隐藏在方法 DomainObjectReader#mergeForPut(请参阅 its source)中 - 它从不用新资源替换相关资源。
在我在Spring JIRA 上发布此问题之前,请在此处报告您是否在项目中遇到此问题以及您对此有何看法。
您可以获取我的测试 here 并在您的项目中进行检查 - 该测试是“独立的”并且不依赖于其他类/模块(我希望仅排除 H2)。
@Entity
public class Person {
private String name;
@ManyToOne
private Address address;
// other stuff
}
@Entity
public class Address {
private String street;
// other stuff
}
尝试更新人:
PUT http://localhost:8080/api/persons/1
{
"name": "person1u",
"address": "http://localhost:8080/api/addresses/2"
}
得到正确的响应:
{
"name": "person1u",
"_links": {
"self": {
"href": "http://localhost:8080/api/persons/1"
},
"person": {
"href": "http://localhost:8080/api/persons/1"
},
"address": {
"href": "http://localhost:8080/api/persons/1/address"
}
}
}
然后检查人员的“新”地址 - 地址未更新:
GET http://localhost:8080/api/persons/1/address
{
"street": "address1",
"_links": {
"self": {
"href": "http://localhost:8080/api/addresses/1"
},
"address": {
"href": "http://localhost:8080/api/addresses/1"
}
}
}
2017 年 8 月 24 日更新
感谢 Scott C.answer,事实证明 SDR 有一个错误,在两张票中进行了描述:DATAREST-1001 和 DATAREST-1012。
【问题讨论】:
-
链接
http://localhost:8080/api/persons/1/address(v.2.5.6) 的对象是什么? -
@AndrewTobilko 地址1:
{ "street": "address1", "_links": { "self": { "href": "http://localhost:8080/api/addresses/1" }, "address": { "href": "http://localhost:8080/api/addresses/1" } } } -
我不明白为什么它适用于第一个版本。应该抛出相同的异常,因为
Address实例不能从单个String构造。BaseEntity是什么? -
@AndrewTobilko 这是实体的base class。所有 ctor 都在场 - 我在项目中使用 Lombok...
-
@halfer 好的。谢谢。
标签: java rest spring-boot spring-data-rest put