如何正确发布到双向关系

Posted

技术标签:

【中文标题】如何正确发布到双向关系【英文标题】:How to POST correctly to a bidirectional relationship 【发布时间】:2019-08-18 19:05:16 【问题描述】:

所以我使用 springboot、spring-data 和 Jackson 实现了一个 API,但是当我尝试向具有 @OneToMany 双向关系的端点发布请求时遇到了一些麻烦。

我没有这么多背景,所以我真的需要帮助。

我有两个实体:

帕蒂达

package lucas.duarte.jazz.model.bean;

import java.io.Serializable;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonProperty;

@Entity
public class Partida implements Serializable 
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String timeA;
    private String timeB;
    private boolean visitante;
    @OneToMany(mappedBy = "partida", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<Set> sets;


    public List<Set> getSets() 
        return sets;
    

    public void setSets(List<Set> sets) 
        this.sets = sets;
    

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getTimeA() 
        return timeA;
    

    // Mocado o valor pois o Time A sempre e a Sao Judas
    public void setTimeA(String timeA) 
        this.timeA = timeA;
    

    public String getTimeB() 
        return timeB;
    

    public void setTimeB(String timeB) 
        this.timeB = timeB;
    

    public boolean isVisitante() 
        return visitante;
    

    public void setVisitante(boolean visitante) 
        this.visitante = visitante;
    



和设置

package lucas.duarte.jazz.model.bean;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@Entity
@Table(name = "meu_set")
public class Set implements Serializable 
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "ponto_a")
    private long pontoA;
    @Column(name = "ponto_b")
    private long pontoB;
    @Column(name = "set_finalizado")
    private boolean setFinalizado;
    @ManyToOne
    @JoinColumn(name = "partida_id")
    private Partida partida;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public long getPontoA() 
        return pontoA;
    

    public void setPontoA(long pontoA) 
        this.pontoA = pontoA;
    

    public long getPontoB() 
        return pontoB;
    

    public void setPontoB(long pontoB) 
        this.pontoB = pontoB;
    

    public boolean isSetFinalizado() 
        return setFinalizado;
    

    public void setSetFinalizado(boolean setFinalizado) 
        this.setFinalizado = setFinalizado;
    

    public Partida getPartida() 
        return partida;
    

    public void setPartida(Partida partida) 
        this.partida = partida;
    



这是我的 SetControler

package lucas.duarte.jazz.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.util.UriComponentsBuilder;

import lucas.duarte.jazz.model.bean.Set;
import lucas.duarte.jazz.model.service.SetService;

@Controller
@RequestMapping("/api")
public class SetController 

    private SetService setServ;

    @RequestMapping(value = "/set/", method = RequestMethod.POST)
    public ResponseEntity<?> salvarSet(@RequestBody Set set, UriComponentsBuilder ucBuilder) 
        System.out.println("Vou cadastrar um set vinculado a uma partida");
        System.out.println(set.getPartida().getId());
        setServ.salvarSet(set);
        return new ResponseEntity(HttpStatus.CREATED);
    



当我发布到 URL 时,我得到以下返回


    "timestamp": "2019-03-28T04:17:46.857+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "JSON parse error: Cannot construct instance of `lucas.duarte.jazz.model.bean.Partida` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `lucas.duarte.jazz.model.bean.Partida` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1)\n at [Source: (PushbackInputStream); line: 5, column: 14] (through reference chain: lucas.duarte.jazz.model.bean.Set[\"partida\"])",
    "path": "/api/set/"

我的数据库中已经有一个 Partida,但我无法 POST 到这个方法,需要帮助。

【问题讨论】:

请定义默认构造函数 public Partida () 为了反序列化的目的,Partida 类必须有一个零参数的构造函数。 显示您尝试发送到此端点的 json 正文 "pontoA" : 1, "pontoB" : 1, "setFinalizado" : "false", "partida_id" : 1 我请求的Json正文 @LucasDuarte try This "partida_id": 1, "pontoA": 1, "pontoB": 1, "setFinalizado": "false" 【参考方案1】:

此问题与 JPA 双向映射无关。它在反序列化时引发错误。 Partida -> 应该有零参数构造函数

请求负载应具有 "partida":"id":123 以填充 partida 对象属性。

【讨论】:

当我尝试这个时,我得到了以下 2019-03-28 02:48:17.679 ERROR 8180 --- [nio-5888-exec-3] occC[.[.[/]。 [dispatcherServlet]:servlet [dispatcherServlet] 的 Servlet.service() 在路径 [] 的上下文中抛出异常 [请求处理失败;嵌套异常是 java.lang.NullPointerException] 的根本原因 你是对的,只是我传递 JSON 的方式,空指针异常是因为我忘记了 @Autowired 注释注入依赖项。 这是个好消息,至少 json 错误消失了。私有 SetService setServ;为null,对象中的任何操作都会抛出空指针异常。【参考方案2】:

详情请查看此链接Jackson library。

@Entity
public class Partida implements Serializable 
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String timeA;
    private String timeB;
    private boolean visitante;
    public  Partida()
    //Default constructor required here 
    
    @OneToMany(mappedBy = "partida", fetch = FetchType.LAZY, cascade = 
    CascadeType.ALL)
    private List<Set> sets;


    public List<Set> getSets() 
        return sets;
    

    public void setSets(List<Set> sets) 
        this.sets = sets;
    

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getTimeA() 
        return timeA;
    

    // Mocado o valor pois o Time A sempre e a Sao Judas
    public void setTimeA(String timeA) 
        this.timeA = timeA;
    

    public String getTimeB() 
        return timeB;
    

    public void setTimeB(String timeB) 
        this.timeB = timeB;
    

    public boolean isVisitante() 
        return visitante;
    

    public void setVisitante(boolean visitante) 
        this.visitante = visitante;
    


如果问题未解决,请尝试 JSON 创建器

@JsonCreator
    public Partida(@JsonProperty("id") Long id, @JsonProperty("timeA") String 
    timeA, @JsonProperty("timeB") String timeB, @JsonProperty("desc") boolean
    visitante) 
    this.id = id;
    this.timeA = timeA;
    this.timeB= timeB;
    this.visitante= visitante;
 

【讨论】:

它也不起作用,我尝试使用空构造函数和注释@JsonCreator,服务器总是同样的错误 @LucasDuarte:在您的集合类 public Set() 中创建一个空构造函数,然后分享您的错误消息

以上是关于如何正确发布到双向关系的主要内容,如果未能解决你的问题,请参考以下文章

hibernate/jpa double OneToOne 与一个实体的双向关系

OneToOne 双向关系的错误映射原则

最佳实践:应该避免双向关系吗? [关闭]

如何在 Kotlin 中使用 JPA ManyToMany 双向关系

如何通过级联级联@ManyToOne 双向关系?

如何与 Spring Data REST 和 JPA 保持双向关系?