Consider the following entities:
package br.com.investors.domain.endereco;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;
import org.hibernate.validator.constraints.NotBlank;
import javax.persistence.*;
import java.io.Serializable;
import static com.google.common.base.Preconditions.checkArgument;
import static javax.persistence.GenerationType.SEQUENCE;
@Entity
public class Regiao implements Serializable, Comparable<Regiao> {
@Id
@GeneratedValue(strategy = SEQUENCE)
private Long id;
@Version
private Long version;
@NotBlank
@Column(length = 100, unique = true)
private String nome = "";
Regiao() {}
public Regiao(String nome) {
checkArgument(!Strings.isNullOrEmpty(nome), "Nome não pode ser vazio");
this.nome = nome;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Regiao) {
Regiao o = (Regiao) obj;
return Objects.equal(this.nome, o.nome);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(nome);
}
@Override
public int compareTo(Regiao o) {
return ComparisonChain.start()
.compare(this.nome, o.nome)
.result();
}
@Override
public String toString() {
return Objects.toStringHelper(getClass()).add("nome", nome).toString();
}
public Long getId() {
return id;
}
public Long getVersion() {
return version;
}
public String getNome() {
return nome;
}
}
and
package br.com.investors.domain.endereco;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain;
import org.hibernate.validator.constraints.NotBlank;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static javax.persistence.GenerationType.SEQUENCE;
@Entity
public class Cidade implements Serializable, Comparable<Cidade> {
@Id
@GeneratedValue(strategy = SEQUENCE)
private Long id;
@Version
private Long version;
@NotBlank
@Column(length = 100, unique = true)
private String nome = "";
@NotNull
@ManyToOne
private Regiao regiao;
@NotNull
@ManyToOne
private Estado estado;
Cidade() {}
public Cidade(String nome, Regiao regiao, Estado estado) {
checkArgument(!Strings.isNullOrEmpty(nome), "Nome não pode ser vazio");
checkNotNull(regiao, "Região não pode ser nulo");
checkNotNull(estado, "Estado não pode ser nulo");
this.nome = nome;
this.regiao = regiao;
this.estado = estado;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Cidade) {
Cidade o = (Cidade) obj;
return Objects.equal(this.nome, o.nome) &&
Objects.equal(this.estado, o.estado) &&
Objects.equal(this.regiao, o.regiao);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(nome, regiao, estado);
}
@Override
public int compareTo(Cidade o) {
return ComparisonChain.start()
.compare(this.estado, o.estado)
.compare(this.regiao, o.regiao)
.compare(this.nome, o.nome)
.result();
}
@Override
public String toString() {
return Objects.toStringHelper(getClass()).add("nome", nome).add("regiao", regiao).add("estado", estado).toString();
}
public Long getId() {
return id;
}
public Long getVersion() {
return version;
}
public String getNome() {
return nome;
}
public Regiao getRegiao() {
return regiao;
}
public Estado getEstado() {
return estado;
}
}
I'm trying to POST a JSON to a RestController
@RequestMapping(value = "/cidades", method = POST, consumes = APPLICATION_JSON_VALUE)
void inserir(@RequestBody Cidade cidade) {
repository.save(cidade);
}
I'm using default configurations of Spring Boot to serialize and deserialize objects.
If I post a JSON like this, it works fine:
{
"nome": "Cidade",
"regiao": "/10"
}
But I need to post a JSON like this:
{
"nome": "Cidade",
"regiao": {
"id": 10,
"version": 0,
"nome": "regiao"
}
}
If I do so, I get the error
{
"timestamp": "2015-04-02",
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "Could not read JSON: Template must not be null or empty! (through reference chain: br.com.investors.domain.endereco.Cidade[\"regiao\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Template must not be null or empty! (through reference chain: br.com.investors.domain.endereco.Cidade[\"regiao\"])",
"path": "/cidades/"
}
Doing some debug, I found that Jackson tries to create a URI from the "regiao" property of the posted object, waiting for a string template like "/{id}". I'm googling it but can't find a properly answer for this.
I saw some related issues on StackOverflow but none worked for me.
Can you guys say what is the matter of this?
I think that is just a configuration but don't know how or where.
I'm also trying to avoid custom serializers and deserializers.
EDIT:
If I POST a JSON with only the ids of the nested entities, like this:
{
"nome": "Cidade",
"estado": "10",
"regiao": "10"
}
I get this message:
{
"timestamp": "2015-04-07",
"status": 400,
"error": "Bad Request",
"exception": "org.springframework.http.converter.HttpMessageNotReadableException",
"message": "Could not read JSON: Failed to convert from type java.net.URI to type br.com.investors.domain.endereco.Estado for value '10'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI 10. Is it local or remote? Only local URIs are resolvable. (through reference chain: br.com.investors.domain.endereco.Cidade[\"estado\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Failed to convert from type java.net.URI to type br.com.investors.domain.endereco.Estado for value '10'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI 10. Is it local or remote? Only local URIs are resolvable. (through reference chain: br.com.investors.domain.endereco.Cidade[\"estado\"])",
"path": "/cidades"
}
As I see that the correct way of send the nested entity is like "regiao": "/10", I'm hardcoding this in my Javascript to workaround:
function(item) {
item.regiao = "/" + item.regiao.id; //OMG
item.estado = "/" + item.estado.id; //OMG!!
if (item.id) {
return $http.put('/cidades/' + item.id, item);
} else {
return $http.post('/cidades', item);
}
}
It works but it sucks. How can I fix this in Javascript or configuring Jackson?
Reading some docs, there is something to do with UriToEntityConverter, but still don't know the correct way of configure this.
Thanks.
I solved it with @RestResource(exported = false) annotation on the EstadoRepository and RegiaoRepository classes.
It "hides" de repo from spring when it's auto config the endpoints and stuff...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With