假设我在前端有一个常规字段和下拉列表。 在这些下拉菜单中,用户可以选择选项,并且每个选项都链接到Spring数据JPA中的实体;
下拉列表包含一些标签以及指向相应实体的链接,作为值。 然后,该值将在POST请求中传递给我们希望创建的实体的 PagingAndSorting存储库。
假设这是一个拥有用户名的用户,并且必须与其中一个办事处(也是一个实体)相关联:
@Data
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name="users")
public class User{
@Id
@Coluemn(name="USER_ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
@Column(name="USER_NAME", nullable=false)
private String userName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="OFFICE_ID", **nullable=false**)
private Office office;
}
我的第一个猜测是: 将 POST - 请求发送到http://localhost:8080/api/users/ 的的contentType : '应用/ JSON'
{"userName":"Anton","office":"http://localhost:8080/api/offices/1"}
但它抛出异常
{
"cause": {
"cause": null,
"message": "Cannot construct instance of `test.domain.Office` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/offices/1')\n at [Source: (org.apache.catalina.connector.CoyoteInputStream); line: 1, column: 160] (through reference chain: test.domain.User[\"office\"])"
},
"message": "JSON parse error: Cannot construct instance of `test.domain.Office` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/offices/1'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `test.domain.Office` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('http://localhost:8080/api/office/1')\n at [Source: (org.apache.catalina.connector.CoyoteInputStream); line: 1, column: 160] (through reference chain: test.domain.User[\"office\"])"
}
我做错了什么?
答案 0 :(得分:0)
您正在发送一个URL资源作为字符串来代替JSON对象,并期望在Spring和jackson之间发生一些魔术以查找该值。当然,这不是正在发生的事情,杰克逊试图将URL的字符串值绑定到Office字段。这当然失败了,因为它不知道如何从字符串创建Office对象。
一种可能的解决方案是区分您的实体对象(代表您的数据库表的对象)和DTO(数据传输对象),在此原因中代表您与客户的合同。执行此操作时,您可以收到如下所示的用户对象:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User{
private Long userId;
private String userName;
private Long officeId;
}
现在您可以简单地发送办公室ID而不是URL,并在您的代码中使用Spring数据存储库来查找office对象。之后,您可以构建您的实体用户对象,就像上面显示的对象一样,并保留它。
答案 1 :(得分:0)
原来是因为我使用了Lombok,它生成了自己的构造函数。 为了使它工作,我只需要像这样设置@AllArgsConstructor:
@AllArgsConstructor(suppressConstructorProperties = true)
现在它按预期工作:
Json发送给http://localhost:8080/api/users:
{
"userName":"Anton",
"office":"http://localhost:8080/api/offices/1"
}
返回:
{
"userName":"Anton",
"_links": {
"self": {
"href": "http://localhost:8080/api/users/28"
},
"user": {
"href": "http://localhost:8080/api/users/28"
},
"office": {
"href": "http://localhost:8080/api/users/28/office"
}
}
}