form - Cómo PUBLICAR entidades anidadas con Spring Data REST
spring data rest custom controller (4)
Aún veo este error con 2.3.0.M1, pero finalmente encontré una solución alternativa.
El problema básico es este: si publica la url de la entidad incrustada en el JSON, funciona. Si publica la entidad embebida JSON real, no lo hace. Intenta deserializar la entidad JSON en un URI, que por supuesto falla.
Parece que el problema está en los dos objetos TypeConstrainedMappingJackson2HttpMessageConverter que el resto de datos de primavera crea en su configuración (en RepositoryRestMvcConfiguration.defaultMessageConverters ()).
Finalmente solucioné el problema configurando los tipos de medios compatibles de messageConverters para que omita esos dos y llegue al plano MappingJackson2HttpMessageConverter, que funciona bien con entidades anidadas.
Por ejemplo, si extiende RepositoryRestMvcConfiguration y agrega este método, cuando envíe una solicitud con contenido-tipo de ''application / json'', golpeará el MappingJackson2HttpMessageConverter en lugar de tratar de deserializar en 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));
}
Eso configura los convertidores de mensajes producidos por defaultMessageConverters () en RepositoryRestMvcConfiguration.
Tenga en cuenta que el objectMapper simple no puede manejar URI en el JSON; igual deberá presionar uno de los dos conversores de mensajes preconfigurados cada vez que pase URI de entidades incorporadas.
Estoy construyendo una aplicación REST de Spring Data y estoy teniendo algunos problemas cuando trato de PUBLICARLO. La entidad principal tiene otras dos entidades relacionadas anidadas.
Hay un objeto "questionary" que tiene muchas respuestas y cada una de estas respuestas tiene muchas respuestas.
Genero un JSON como este desde la aplicación frontal para enviar el cuestionario:
{
"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"
}
]
}
]
}
Pero cuando intento publicarlo, aparece la siguiente respuesta de error:
{
"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/"])"
}
Editar:
También agrego mi repositorio:
@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);
}
Mi clase de pregunta de entidad es:
@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;
}
}
Mi respuesta Entidad:
@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;
}
}
Y esta es la consola de error:
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*
Intente actualizar la biblioteca "Spring Boot Data REST Starter". Trabajó para mi.
Intente agregar @RestResource(exported = false)
en las answers
campo de la clase Questionary
.
Según mi opinión, este error se produce porque el deserializador espera que los URI obtengan las respuestas, en lugar de tener las respuestas anidadas en el JSON. Agregar la anotación le dice que busque en JSON en su lugar.
Un problema con su JSON es que está tratando de deserializar una cadena como una pregunta:
"question": "http://localhost:8080/question/6"
En su objeto Answer
, Jackson espera un objeto para la pregunta. Parece que está utilizando URL para ID, por lo que en lugar de una cadena necesita pasar algo como esto para su pregunta:
"question": {
"id": "http://localhost:8080/question/6"
}