How do I return in json to Foreign key in Spring Boot?

Asked

Viewed 1,110 times

2

As you can see it is returning to the cities without any problems in the following URL: http://localhost:8080/cidades, watch:

inserir a descrição da imagem aqui

This is a return coming straight from the database with a select * from cidade, you can see some difference?

inserir a descrição da imagem aqui

They realize that in the attribute status code appears in the SQL performed in the database, but this same attribute does not appear in the list made by the Java API of Spring Boot, as it would also like the attribute status code was also returned in the Json of the Java API.

How do I get the API to achieve returns this attribute?

Here below are the responding codes the list returned by the URL: http://localhost:8080/cidades

The controller:

package com.algaworks.brewer.resource;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.algaworks.brewer.model.Cidade;
import com.algaworks.brewer.model.Estado;
import com.algaworks.brewer.repository.CidadeRepository;
import com.algaworks.brewer.repository.EstadoRepository;
import com.algaworks.brewer.repository.filter.CidadeFilter;

@RestController
@RequestMapping("/cidades")
public class CidadeResource {


    @Autowired
    private  CidadeRepository cidadeRepository;

    @Autowired
    private  EstadoRepository estadoRepository;


    @GetMapping
    public List<Cidade> pesquisarPorCidade(CidadeFilter cidadeFilter){
        return cidadeRepository.filtrar(cidadeFilter);
    }

    @GetMapping("/estados")
    public List<Estado> pesquisarPorEstado(){
        return estadoRepository.findAll();
    }


}

The repository:

package com.algaworks.brewer.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.algaworks.brewer.model.Cidade;
import com.algaworks.brewer.repository.cidade.CidadeRepositoryQuery;

public interface CidadeRepository extends JpaRepository<Cidade, Long> , CidadeRepositoryQuery {


}

The filter implementation interface:

package com.algaworks.brewer.repository.cidade;

import java.util.List;

import com.algaworks.brewer.model.Cidade;
import com.algaworks.brewer.repository.filter.CidadeFilter;

public interface CidadeRepositoryQuery {

        public List<Cidade> filtrar(CidadeFilter cidadeFilter);
}

The implementation itself:

package com.algaworks.brewer.repository.cidade;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.util.StringUtils;

import com.algaworks.brewer.model.Cidade;
import com.algaworks.brewer.model.Cidade_;
import com.algaworks.brewer.repository.filter.CidadeFilter;

public class CidadeRepositoryImpl implements CidadeRepositoryQuery {


    @PersistenceContext
    private EntityManager manager;

    @Override
    public List<Cidade> filtrar(CidadeFilter cidadeFilter) {
        CriteriaBuilder builder = manager.getCriteriaBuilder();
        CriteriaQuery<Cidade> criteria = builder.createQuery(Cidade.class);
        Root<Cidade> root = criteria.from(Cidade.class);

        Predicate[] predicates = criarRestricoes(cidadeFilter, builder, root);
        criteria.where(predicates);

        TypedQuery<Cidade> query = manager.createQuery(criteria);
        return query.getResultList();
    }


    private Predicate[] criarRestricoes(CidadeFilter cidadeFilter, CriteriaBuilder builder,
            Root<Cidade> root) {
        List<Predicate> predicates = new ArrayList<>();

        if (!StringUtils.isEmpty(cidadeFilter.getNome())) {
            predicates.add(builder.like(
                    builder.lower(root.get(Cidade_.nome)), "%" + cidadeFilter.getNome().toLowerCase() + "%"));
        }





        return predicates.toArray(new Predicate[predicates.size()]);
    }


}





//if (cidadeFilter.getEstado() != null) {
//  predicates.add(
//          builder.greaterThanOrEqualTo(root.get(Cidade_.estado.getName()), cidadeFilter.getEstado().getNome()));
//}

I look forward to the return!

That’s the city code:

package com.algaworks.brewer.model;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.FetchType;
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 javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotBlank;

import com.fasterxml.jackson.annotation.JsonIgnore;

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long codigo;

    @NotBlank(message = "Nome é obrigatório")
    private String nome;

    @NotNull(message = "Estado é obrigatório")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "codigo_estado")
    @JsonIgnore
    private Estado estado;

    public Long getCodigo() {
        return codigo;
    }

    public void setCodigo(Long codigo) {
        this.codigo = codigo;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public Estado getEstado() {
        return estado;
    }

    public void setEstado(Estado estado) {
        this.estado = estado;
    }

    public boolean temEstado() {
        return estado != null;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((codigo == null) ? 0 : codigo.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Cidade other = (Cidade) obj;
        if (codigo == null) {
            if (other.codigo != null)
                return false;
        } else if (!codigo.equals(other.codigo))
            return false;
        return true;
}
}

And the State entity:

package com.algaworks.brewer.model;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "estado")
public class Estado implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long codigo;
    private String nome;
    private String sigla;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getCodigo() {
        return codigo;
    }

    public void setCodigo(Long codigo) {
        this.codigo = codigo;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getSigla() {
        return sigla;
    }

    public void setSigla(String sigla) {
        this.sigla = sigla;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((codigo == null) ? 0 : codigo.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Estado other = (Estado) obj;
        if (codigo == null) {
            if (other.codigo != null)
                return false;
        } else if (!codigo.equals(other.codigo))
            return false;
        return true;
    }
}

You can see that there is one @JsonIgnore in the private Estado estado it’s like being ignored, I took this code from somewhere else that’s why it’s there, I need to make a change time in such a way that the LAZY still work.

see the excerpt:

@NotNull(message = "Estado é obrigatório")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "codigo_estado")
    @JsonIgnore
    private Estado estado;
  • You can post your entity code Cidade? I think you should be mapping Estado in a relationship @ManyToOne (which is correct in JPA). To do what you want you will probably have to create a new field codigoEstado with the annotation @Column(name="codigo_estado", updatable=false, insertable=false) but this is not the best of ideas (in practice the relational model is "leaking" to higher layers of application)

  • 1

    @wladyband Post to us its entities. It would be of great value to evaluate how are the relationships and mappings.

  • @Youngerauad , I just updated the post by putting the code of the two entities, could you take a look at me please?

  • @Anthonyaccioly take a look please? I updated the post

  • 1

    @wladyband, try my suggestion from the above comment: @Column(name="codigo_estado", updatable=false, insertable=false) private Long codigoEstado; in practice but perhaps it is better to create a different object for the service and copy the necessary values of City and State.

1 answer

2


@wladyband,

If I understand your problem correctly, you would like to display the state code next to the city data. Also, you want to also keep the @JsonIgnore in the relationship @ManyToOne with the State class, therefore, does not seem to want to prevent all the state object being converted and all the state data being next to the city, but only its code.


OBS: As a matter of curiosity, since version 2.6 of Jackson (Json processing library, which is default in Spring MVC), we have a more intuitive annotation than the @JsonIgnore, that is @JsonProperty and that you will be able to control the access in more detail:

@JsonProperty(access = Access.WRITE_ONLY)
private String myField;

@JsonProperty(access = Access.WRITE_ONLY)
private String myField2;

@JsonProperty(access = Access.READ_WRITE)
private String myField3;

Take a closer look at documentation of that note.


Within all that I realized you want to do, you will only have the option of creating a new attribute within the city class to contain only the state code, without performing relationship @ManyToOne, and use no ignore annotations for it to be serialized. In this field we will make treatment so that it is not used neither to be inserted in the bank nor to update anything related in the bank, that is, it will be a field Transient or just for reading. It follows below as it would be:

package com.algaworks.brewer.model;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.FetchType;
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 javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotBlank;

import com.fasterxml.jackson.annotation.JsonIgnore;

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long codigo;

    @NotBlank(message = "Nome é obrigatório")
    private String nome;

    @NotNull(message = "Estado é obrigatório")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "codigo_estado")
    @JsonIgnore
    private Estado estado;

    @Column(name="codigo_estado", updatable=false, insertable=false) 
    private Long codigoEstado;

    public Long getCodigo() {
        return codigo;
    }

    public void setCodigo(Long codigo) {
        this.codigo = codigo;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public Estado getEstado() {
        return estado;
    }

    public void setEstado(Estado estado) {
        this.estado = estado;
    }

    public boolean temEstado() {
        return estado != null;
    }

    public Long getCodigoEstado() {
        return codigoEstado;
    }

    public void setCodigoEstado(Long codigoEstado) {
        this.codigoEstado = codigoEstado;
    }

    //........
}

Anything, please report so I can try to improve the explanation. I do not know if I have captured all your need and restriction.

  • It was right, the way I wanted it, thank you very much! :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.