【问题标题】:How to create a new entity with association如何创建具有关联的新实体
【发布时间】:2018-10-28 18:20:08
【问题描述】:

假设我在前端有一个包含常用字段和下拉列表的表单。 在这些下拉菜单中,用户可以选择一个 option,并且每个 option 都链接到 Spring data JPA 中的一个 entity

下拉菜单包含一些标签和对应实体的链接作为。 然后,该值会在 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:'application/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\"])"
}

我做错了什么?

【问题讨论】:

    标签: ajax spring spring-mvc spring-data-jpa spring-rest


    【解决方案1】:

    您正在发送一个 URL 资源作为字符串代替 JSON 对象,并期望在 Spring 和 jackson 之间发生一些魔术来查找该值。当然这不是正在发生的事情,Jackson 正在尝试将 URL 的字符串值绑定到 Office 字段。这当然会失败,因为它不知道如何从字符串创建 Office 对象。

    一个可能的解决方案是区分您的实体对象(那些代表您的数据库表)和 DTO(数据传输对象),这代表您与客户的合同。执行此操作时,您可能会收到这样的用户对象:

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class User{
    
      private Long userId;
    
      private String userName;
    
      private Long officeId;
    
    }
    

    现在您可以简单地发送 office id 而不是 URL,并在您的代码中使用 Spring 数据存储库来查找 office 对象。之后,您可以像上面显示的那样构造您的 Entity User 对象并将其持久化。

    【讨论】:

    • 但是带有 contentType:'application/json' 的 PATCH 请求工作得很好,即使链接作为字符串传递,除了它不能创建实体,只能修补它们:{ "userName":"Anton", "Office": "http://localhost:8080/api/offices/1" }
    • 当然可以,用户无法从下拉菜单中选择不存在的选项
    • 也许我在传递 URL 代替 JSON 对象方面是错误的,但杰克逊显然试图将字符串反序列化为 Office 对象并失败。为什么不试试我的建议,看看它是否适合你。
    【解决方案2】:

    原来是因为我使用了 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" } } }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      • 2013-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多