Closed mandado closed 9 years ago
I am not sure what the question here is. Would it be possible to have a bit of code that uses this model, exposes the issue?
Method is listaratividades() , i'm was used spring data to abstract repository code. i was debug code and see that spring data returns all data correctly with relationship, but relationship not injected a json response.
package app.controller;
import app.lib.ValidationMessage;
import app.model.Atividade;
import app.model.Local;
import app.repository.AtividadeRepository;
import app.repository.LocalRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
/**
* Created by jorge on 27/05/15.
*/
@RestController
@RequestMapping("api/")
public class AtividadeController {
@Autowired
private AtividadeRepository repository;
@Autowired
private LocalRepository localRepository;
@RequestMapping(value = "atividade", method = RequestMethod.POST)
@ResponseBody public ResponseEntity novoatividade(@RequestBody @Valid Atividade atividade, BindingResult result) {
Local local = localRepository.findOne(atividade.getLocal().getId());
atividade.setLocal(local);
if (result.hasErrors()) {
validate(result);
}
return new ResponseEntity(repository.save(atividade), HttpStatus.OK);
}
@RequestMapping(value = "atividade", method = RequestMethod.GET)
@ResponseBody
public Iterable<Atividade> listaratividades() {
return repository.findAll();
}
@RequestMapping(value = "atividade/{id}", method = RequestMethod.GET)
public Atividade buscaratividade(@PathVariable Long id) {
return repository.findOne(id);
}
@RequestMapping(value = "atividade/{id}", method = RequestMethod.DELETE)
public void deletaratividade(@PathVariable Long id) {
repository.delete(id);
}
@RequestMapping(value = "atividade", method = RequestMethod.PUT)
public ResponseEntity editaratividade(@RequestBody @Valid Atividade atividade, BindingResult result) {
if (result.hasErrors()) {
validate(result);
}
return new ResponseEntity(repository.save(atividade), HttpStatus.OK);
}
private ResponseEntity validate(BindingResult result) {
ArrayList<String> errors = ValidationMessage.parse(result.getAllErrors());
return new ResponseEntity(errors, HttpStatus.BAD_REQUEST);
}
}
package app.repository;
import app.model.Atividade;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
/**
* Created by jorge on 01/06/15.
*/
@Component
@Repository
public interface AtividadeRepository extends CrudRepository<Atividade,Long> {
}
Unfortunately I am not familiar with Spring, so I would need a stand-alone test case.
One thing to note on managed/back reference is that it must always be traversed from managed to back reference: meaning that parent object must be serialized first. If traversal may be in either direction, this approach will not work, and the more generic Object Id handling (via @JsonIdentityInfo
, usually) is needed.
but @jsonIdentityInfo
returns this error
Could not read JSON: Unresolved forward references for: Object id [42] (for class app.model.Local) at [Source: java.io.PushbackInputStream@17ad297; line: 1, column: 343].; nested exception is com.fasterxml.jackson.databind.deser.UnresolvedForwardReference: Unresolved forward references for: Object id
when execute insert or update operation.
I mapped Atividade and Local model with @jsonIdentityInfo
package app.model;
import com.fasterxml.jackson.annotation.*;
import org.springframework.data.jpa.domain.AbstractPersistable;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Created by jorge on 26/04/15.
*/
@Entity
@Table(name = "atividade")
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class Atividade extends AbstractPersistable<Long>{
@Column(name = "nome")
private String nome;
@Column(name = "descricao")
private String descricao;
@Column(name = "tipo")
private Long tipo;
@Column(name = "prerequisito")
private String prerequisito;
@Column(name = "data")
@Temporal(TemporalType.DATE)
private Date data;
@Column(name = "hora_inicio")
@Temporal(TemporalType.TIME)
private Date hora_inicio;
@Column(name = "hora_fim")
@Temporal(TemporalType.TIME)
private Date hora_fim;
@Column(name = "inscricoes_inicio")
@Temporal(TemporalType.DATE)
private Date inscricoes_inicio;
@Column(name = "inscricoes_fim")
@Temporal(TemporalType.DATE)
private Date inscricoes_fim;
@Column(name = "valor_investimento")
private BigDecimal valor_investimento;
@Column(name = "valor_desconto")
private BigDecimal valor_desconto;
@ManyToOne
@JoinColumn(name = "local_id")
private Local local;
@ManyToMany
@JoinTable(name = "palestrante_atividade",
joinColumns = {
@JoinColumn(name = "id_atividade")
},inverseJoinColumns = {
@JoinColumn(name = "id_palestrante")
})
private List<Palestrante> palestrantes = new ArrayList<Palestrante>();
public Atividade() {
}
public Atividade(String nome, String descricao, Long tipo, String prerequisito, Date data, Date hora_inicio, Date hora_fim, Date inscricoes_inicio, Date inscricoes_fim, BigDecimal valor_investimento, BigDecimal valor_desconto) {
this.nome = nome;
this.descricao = descricao;
this.tipo = tipo;
this.prerequisito = prerequisito;
this.data = data;
this.hora_inicio = hora_inicio;
this.hora_fim = hora_fim;
this.inscricoes_inicio = inscricoes_inicio;
this.inscricoes_fim = inscricoes_fim;
this.valor_investimento = valor_investimento;
this.valor_desconto = valor_desconto;
}
public Atividade(String nome, String descricao, Long tipo, String prerequisito, Date data, Date hora_inicio, Date hora_fim, Date inscricoes_inicio, Date inscricoes_fim, BigDecimal valor_investimento, BigDecimal valor_desconto, Local local, ArrayList<Palestrante> palestrantes) {
this.nome = nome;
this.descricao = descricao;
this.tipo = tipo;
this.prerequisito = prerequisito;
this.data = data;
this.hora_inicio = hora_inicio;
this.hora_fim = hora_fim;
this.inscricoes_inicio = inscricoes_inicio;
this.inscricoes_fim = inscricoes_fim;
this.valor_investimento = valor_investimento;
this.valor_desconto = valor_desconto;
this.local = local;
this.palestrantes = palestrantes;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public Long getTipo() {
return tipo;
}
public void setTipo(Long tipo) {
this.tipo = tipo;
}
public String getPrerequisito() {
return prerequisito;
}
public void setPrerequisito(String prerequisito) {
this.prerequisito = prerequisito;
}
public Date getData() {
return data;
}
public void setData(Date data) {
this.data = data;
}
public Date getHora_inicio() {
return hora_inicio;
}
public void setHora_inicio(Date hora_inicio) {
this.hora_inicio = hora_inicio;
}
public Date getHora_fim() {
return hora_fim;
}
public void setHora_fim(Date hora_fim) {
this.hora_fim = hora_fim;
}
public Date getInscricoes_inicio() {
return inscricoes_inicio;
}
public void setInscricoes_inicio(Date inscricoes_inicio) {
this.inscricoes_inicio = inscricoes_inicio;
}
public Date getInscricoes_fim() {
return inscricoes_fim;
}
public void setInscricoes_fim(Date inscricoes_fim) {
this.inscricoes_fim = inscricoes_fim;
}
public BigDecimal getValor_investimento() {
return valor_investimento;
}
public void setValor_investimento(BigDecimal valor_investimento) {
this.valor_investimento = valor_investimento;
}
public BigDecimal getValor_desconto() {
return valor_desconto;
}
public void setValor_desconto(BigDecimal desconto_aluno) {
this.valor_desconto = desconto_aluno;
}
public Local getLocal() {
return local;
}
public void setLocal(Local local) {
this.local = local;
}
public List<Palestrante> getPalestrantes() {
return palestrantes;
}
public void setPalestrantes(ArrayList<Palestrante> palestrantes) {
this.palestrantes = palestrantes;
}
}
package app.model;
import com.fasterxml.jackson.annotation.*;
import org.springframework.data.jpa.domain.AbstractPersistable;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/**
* Created by jorge on 26/04/15.
*/
@Entity
@Table(name = "local")
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class Local extends AbstractPersistable<Long>{
@Column(name = "cnpj")
private String cnpj;
@Column(name = "nome")
private String nome;
@Column(name = "tel")
private String tel;
@Column(name = "rua")
private String rua;
@Column(name = "bairro")
private String bairro;
@Column(name = "cidade")
private String cidade;
@Column(name = "estado")
private String estado;
@Column(name = "descricao")
private String descricao;
@Column(name = "capacidade_lotacao")
@JsonProperty("capacidade_lotacao")
private Long capacidade_lotacao;
@OneToMany(mappedBy = "local", targetEntity = Atividade.class,cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Atividade> atividades;
public Local() {
}
public Local(Long id) {
this.setId(id);
}
public Local( String cnpj, String nome, String tel, String rua, String bairro, String cidade, String estado, String descricao, Long capacidade_lotacao) {
this.cnpj = cnpj;
this.nome = nome;
this.tel = tel;
this.rua = rua;
this.bairro = bairro;
this.cidade = cidade;
this.estado = estado;
this.descricao = descricao;
this.capacidade_lotacao = capacidade_lotacao;
}
public Local( String cnpj, String nome, String tel, String rua, String bairro, String cidade, String estado, String descricao, Long capacidade_lotacao, List<Atividade> atividades) {
this.cnpj = cnpj;
this.nome = nome;
this.tel = tel;
this.rua = rua;
this.bairro = bairro;
this.cidade = cidade;
this.estado = estado;
this.descricao = descricao;
this.capacidade_lotacao = capacidade_lotacao;
this.atividades = atividades;
}
public String getCnpj() {
return cnpj;
}
public void setCnpj(String cnpj) {
this.cnpj = cnpj;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getRua() {
return rua;
}
public void setRua(String rua) {
this.rua = rua;
}
public String getBairro() {
return bairro;
}
public void setBairro(String bairro) {
this.bairro = bairro;
}
public String getCidade() {
return cidade;
}
public void setCidade(String cidade) {
this.cidade = cidade;
}
public String getEstado() {
return estado;
}
public void setEstado(String estado) {
this.estado = estado;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public Long getCapacidade_lotacao() {
return capacidade_lotacao;
}
public void setCapacidade_lotacao(Long capacidade_lotacao) {
this.capacidade_lotacao = capacidade_lotacao;
}
public List<Atividade> getAtividades() {
if(atividades == null){
atividades = new ArrayList<Atividade>();
}
return atividades;
}
public void setAtividades(List<Atividade> atividades) {
this.atividades = atividades;
}
}
Correct: you must provide an id to be able to serialize or deserialize Object Ids.
what do you mean ? id must come model ? in my collection of atividade have id.
[
{
"@id": 1,
"id": 43,
"nome": "teste",
"descricao": "teste",
"tipo": 1,
"prerequisito": "teste",
"data": "1499-12-31",
"hora_inicio": "00:01:00",
"hora_fim": "00:01:00",
"inscricoes_inicio": "1970-01-01",
"inscricoes_fim": "1970-01-01",
"valor_investimento": 12,
"valor_desconto": 12,
"local": {
"@id": 1,
"id": 42,
"cnpj": "123456",
"nome": "teste",
"tel": "123456",
"rua": "456",
"bairro": "123",
"cidade": "123",
"estado": "mg",
"descricao": "1234",
"atividades": [
1,
2
],
"new": false,
"capacidade_lotacao": 1023456
},
"palestrantes": [],
"new": false
},
{
"@id": 3,
"id": 44,
"nome": "testador",
"descricao": "123",
"tipo": 1,
"prerequisito": "1",
"data": "1995-06-10",
"hora_inicio": "00:01:00",
"hora_fim": "00:00:00",
"inscricoes_inicio": "1970-01-01",
"inscricoes_fim": "1970-01-01",
"valor_investimento": 1,
"valor_desconto": 1,
"local": 1,
"palestrantes": [],
"new": false
}
]
What I am saying that the exception you mention indicates that an object id reference found from JSON did not point to id of any other Object within JSON. In this case,
Looking at JSON, it looks like there are 2 id properties; "id" and "@id". Maybe that is not intended, and only "id" should be used? If so, @JsonIdentityInfo
annotation should use that.
It depends on whether you want to generate different ids than those stored in the database (use just one id), or separate ones (use id
and @id
).
As things are configured I guess you do want to use separate ids, and this is why '@id' is added.
Looking at class definition and JSON, exception does not look right however: 42 is never used as a reference. Is it possible that you may have "id" in @JsonIdentityInfo
locally, and not like class definition above?
what do you mean with @JsonIdentityInfo locally ? i changed @id to id, because i resolved to use one id only to store in database. but 42 is reference from local relationship and i got the same error of Unsolved forward references...
.
@JsonManagedReference and @JsonBackReference should breaking cyclic reference in json and solve this, without using @JsonIdentityInfo ?
JsonManagedReference and JsonBackReference are mechanims to transparently handle simple uni-directional parent/child relationship, without adding or using ids. But they can not handle arbitrary cyclic relationships, where there is no natural parent/child relation. I am assuming that your problem is that sometimes you are serializing things starting with child, othertimes with parent: if so, it will not work. But your example is big enough that I am not sure. Cutting code size down to just id and a single field, and dropping database-dependant annotations would help.
If you do need general handling of cyclic dependencies, @JsonIdentityInfo
can do that, but it then requires use of ids; either existing ones (provided by POJOs), or generated ones (where Jackson generates object id as metadata, and POJO does NOT have matching property).
As to id
vs @id
, as long as you do want two sets of ids that is fine. I just wanted to verify that was intentional and not accidental. Either usage (one or two) is valid in itself.
two ids was accidental, so it was changed @JsonIdentityInfo (generator = ObjectIdGenerators.IntSequenceGenerator.class, property =" @ id ')
para @JsonIdentityInfo (generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id" )
.
but still keeps giving problem. my intention was to use only id.
At this point what is needed is a smaller reproducible test case. It should be filed against jackson-databind
, because while this package adds anntoations, databind actually handles them.
For usage questions, please use google groups (https://groups.google.com/forum/#!forum/jackson-user).
There is also commercial support for jackson via FasterXML (info@fasterxml.com
) available.
At this point I do not answer questions via my personal email since the volume has gotten high, and all of that support is unpaid work and is starting to compete with my job.
Thanks, i go to post in google groups.
Saw your messages, good -- I hope the issue gets resolved, and will try to help as well.
I have a relationship oneToMany (Local has many Atividades), was used @JsonBackReference and @JsonManagedReference to break cyclic references, but @JsonBackReference doesn't return Local relationship in atividade model,but reverse works fine, is a bug ? or i forgot something?
Local Model
Atividade model