如何使用Spring Data REST POST嵌套实体

时间:2014-07-04 07:58:17

标签: spring rest post jackson

我正在构建一个Spring Data REST应用程序,当我尝试POST它时,我遇到了一些问题。主实体有其他两个相关实体嵌套。

有一个“问号”对象有很多答案,每个答案都有很多回复。

我从前端应用程序生成一个类似这样的JSON来POST问题:

{
    "user": "http://localhost:8080/users/1",
    "status": 1,
    "answers": [
        {
            "img": "urlOfImg",
            "question": "http://localhost:8080/question/6",
            "replies": [
                {
                    "literal": "http://localhost:8080/literal/1",
                    "result": "6"
                },
                {
                    "literal": "http://localhost:8080/literal/1",
                    "result": "6"
                }
            ]
        },
        {
            "img": "urlOfImg",
            "question": "http://localhost:8080/question/6",
            "replies": [
                {
                    "literal": "http://localhost:8080/literal/3",
                    "result": "10"
                }
            ]
        }
    ]
}

但是当我尝试发布它时,我得到了以下错误响应:

{

    "cause" : {
        "cause" : {
          "cause" : null,
          "message" : "Template must not be null or empty!"
        },
        "message" : "Template must not be null or empty! (through reference chain: project.models.Questionary[\"answers\"])"
      },
      "message" : "Could not read JSON: Template must not be null or empty! (through reference chain: project.models.Questionary[\"answers\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Template must not be null or empty! (through reference chain: project.models.Questionary[\"answers\"])"
}

编辑:

我还添加了我的存储库:

@RepositoryRestResource(collectionResourceRel = "questionaries", path = "questionaries")
public interface InspeccionRepository extends JpaRepository<Inspeccion, Integer> {
    @RestResource(rel="byUser", path="byUser")
    public List<Questionary> findByUser (@Param("user") User user);
}

我的实体问题类是:

@Entity @Table(name="QUESTIONARY", schema="enco" )
public class Questionary implements Serializable {
     private static final long serialVersionUID = 1L;
     //----------------------------------------------------------------------
     // ENTITY PRIMARY KEY ( BASED ON A SINGLE FIELD )
     //----------------------------------------------------------------------
     @Id
     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEC_QUESTIONARY")
     @SequenceGenerator(name = "SEC_QUESTIONARY", sequenceName = "ENCO.SEC_QUESTIONARY", allocationSize = 1)
     @Column(name="IDQUES", nullable=false)
     private Integer idques        ;

     //----------------------------------------------------------------------
     // ENTITY DATA FIELDS 
     //----------------------------------------------------------------------    

     @Column(name="ESTATUS")
     private Integer estatus       ;


     //----------------------------------------------------------------------
     // ENTITY LINKS ( RELATIONSHIP )
     //----------------------------------------------------------------------

     @ManyToOne
     @JoinColumn(name="IDUSER", referencedColumnName="IDUSER")
     private User user;

     @OneToMany(mappedBy="questionary", targetEntity=Answer.class)
     private List<Answer> answers;



     //----------------------------------------------------------------------
     // CONSTRUCTOR(S)
     //----------------------------------------------------------------------
     public Questionary()
     {
        super();
     }


     //----------------------------------------------------------------------
     // GETTERS & SETTERS FOR FIELDS
     //----------------------------------------------------------------------

     //--- DATABASE MAPPING : IDNSE ( NUMBER ) 
     public void setIdnse( Integer idnse )
     {
         this.idnse = idnse;
     }
     public Integer getIdnse()
     {
         return this.idnse;
     }

     //--- DATABASE MAPPING : ESTADO ( NUMBER ) 
     public void setEstatus Integer estatus )
     {
         this.estatus = estatus;
     }
     public Integer getEstatus()
     {
         return this.estatus;
     }      
     //----------------------------------------------------------------------
     // GETTERS & SETTERS FOR LINKS
     //----------------------------------------------------------------------
     public void setUser( Usuario user )
     {
         this.user = user;
     }
     public User getUser()
     {
         return this.user;
     }


     public void setAnswers( List<Respuesta> answers )
     {
         this.answers = answer;
     }
     public List<Answer> getAnswers()
     {
         return this.answers;
     }


     // Get Complete Object method      public List<Answer>
     getAnswerComplete() {
         List<Answer> answers = this.answers;
         return answers;
    }
}

我的答案实体:

 @Entity @Table(name="ANSWER", schema="enco" ) public class Answer
 implements Serializable {
     private static final long serialVersionUID = 1L;

     //----------------------------------------------------------------------
     // ENTITY PRIMARY KEY ( BASED ON A SINGLE FIELD )
     //----------------------------------------------------------------------
     @Id
     @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEC_ANSWER")
     @SequenceGenerator(name = "SEC_ANSWER", sequenceName = "ENCOADMIN.SEC_ANSWER", allocationSize = 1)
     @Column(name="IDANS", nullable=false)
     private Integer idans        ;


     //----------------------------------------------------------------------
     // ENTITY DATA FIELDS 
     //----------------------------------------------------------------------    

     @Column(name="IMG", length=100)
     private String     img       ;


     //----------------------------------------------------------------------
     // ENTITY LINKS ( RELATIONSHIP )
     //----------------------------------------------------------------------
     @ManyToOne
     @JoinColumn(name="IDQUES", referencedColumnName="IDQUES")
     private Questionary questionary  ;

     @OneToMany(mappedBy="answer", targetEntity=Reply.class)
     private List<Reply> replies;

     @ManyToOne
     @JoinColumn(name="IDQUE", referencedColumnName="IDQUE")
     private Question Question    ;


     //----------------------------------------------------------------------
     // CONSTRUCTOR(S)
     //----------------------------------------------------------------------
     public Answer()
     {
        super();
     }

     //----------------------------------------------------------------------
     // GETTER & SETTER FOR THE KEY FIELD
     //----------------------------------------------------------------------
     public void setIdans( Integer idans )
     {
         this.idans = idans ;
     }
     public Integer getIdans()
     {
         return this.idans;
     }

     //----------------------------------------------------------------------
     // GETTERS & SETTERS FOR FIELDS
     //----------------------------------------------------------------------

     //--- DATABASE MAPPING : IMAGEN ( VARCHAR2 ) 
     public void setImg( String img )
     {
         this.img = img;
     }
     public String getImg()
     {
         return this.img;
     }


     //----------------------------------------------------------------------
     // GETTERS & SETTERS FOR LINKS
     //----------------------------------------------------------------------
     public void setQuestionary( Questionary questionary )
     {
         this.questionary = questionary;
     }
     public Questionary getQuestionary()
     {
         return this.questionary;
     }

     public void setReplies( List<Reply> contestaciones )
     {
         this.replies = replies;
     }
     public List<Reply> getReplies()
     {
         return this.replies;
     }

     public void setQuestion( Question question )
     {
         this.question = question;
     }
     public Question getQuestion()
     {
         return this.question;
     }


}

这是错误控制台:

Caused by: com.fasterxml.jackson.databind.JsonMappingException:
Template must not be null or empty! (through reference chain:
project.models.Questionary["answers"])  at
  com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:232)
 ~[jackson-databind-2.3.3.jar:2.3.3]    at *snip*

4 个答案:

答案 0 :(得分:17)

尝试在课程@RestResource(exported = false)中的字段answers上添加Questionary

据我所知,出现此错误是因为反序列化器需要URI来获取答案,而不是将答案嵌套在JSON中。添加注释会使其以JSON格式查找。

答案 1 :(得分:5)

我仍然看到2.3.0.M1的这个错误,但我终于找到了解决方法。

基本问题是:如果你在JSON中发布嵌入实体的url,它就可以了。如果您发布实际的嵌入式实体JSON,则不会。它试图将实体JSON反序列化为URI,这当然是失败的。

看起来问题在于Spring数据休息在其配置中创建的两个TypeConstrainedMappingJackson2HttpMessageConverter对象(在RepositoryRestMvcConfiguration.defaultMessageConverters()中)。

我终于通过配置messageConverters支持的媒体类型来解决这个问题,以便它跳过这两个并点击普通的MappingJackson2HttpMessageConverter,它可以与嵌套实体一起使用。

例如,如果扩展RepositoryRestMvcConfiguration并添加此方法,那么当您发送内容类型为“application / json”的请求时,它将触及普通的MappingJackson2HttpMessageConverter,而不是尝试反序列化为URI:

@Override
public void configureHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    ((MappingJackson2HttpMessageConverter) messageConverters.get(0))
            .setSupportedMediaTypes(asList(MediaTypes.HAL_JSON));
    ((MappingJackson2HttpMessageConverter) messageConverters.get(2))
            .setSupportedMediaTypes(asList(MediaType.APPLICATION_JSON));
}

用于配置RepositoryRestMvcConfiguration中defaultMessageConverters()生成的消息转换器。

请记住,普通的objectMapper无法处理JSON中的URI - 每次传递嵌入式实体的URI时,您仍需要点击两个预配置的消息转换器中的一个。

答案 2 :(得分:4)

您的JSON的一个问题是您尝试将字符串反序列化为一个问题:

"question": "http://localhost:8080/question/6"

在你的Answer对象中,杰克逊期待一个问题的对象。您似乎正在使用ID的URL,因此您需要为您的问题传递类似的字符串而不是字符串:

"question": {
    "id": "http://localhost:8080/question/6"
}

答案 3 :(得分:0)

尝试更新“Spring Boot Data REST Starter”库。为我工作。