Solucionado (ver solução)
Solucionado
(ver solução)
11
respostas

Dúvida com relacionamentos entre tabelas

Olá

Tenho duas entidades no meu modelo (Venda e Categoria), categoria possui o id que é chave primaria, e venda possui categoria_id, na hora que realizar um post em venda, está solicitando que eu envie o json completo, inclusive dos campos da classe categoria, eu gostaria de saber se é possível enviar apenas o id.

eu queria passar assim o JSON { "dataVenda": "2021-12-23T00:00:00", "categoria": "1", "comprador": "1", "local": "1", "formaDePagamento": "2", "valorTotal": 180 }

mais ele esta me pedindo pra passar assim

{ "idVenda": 1, "dataVenda": "2021-12-21T16:14:52", "categoria": { "idCategoria": 1, "descricaoCategoria": "venda" }, "comprador": { "idComprador": 1, "nomeComprador": "Alexandre Faria", "cpfComprador": "45522556811", "telefoneComprador": "18997096562", "emailComprador": "alefarria@gmail.com" }, "local": { "idLocal": 1, "descricaoLocal": "Penze Diferente", "endereco": "teste", "telefoneLocal": "33216060" }, "formaDePagamento": { "idPagamento": 2, "descricaoPagamento": "Pix" }, "valorTotal": 180 },

Segue as classes

@Entity public class Venda {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idVenda;
private String dataVenda;

@JsonIgnore
@ManyToOne
@JoinColumn(name = "categoria_id")
private Categoria categoria;

private Integer comprador;
private Integer local;
private Integer formaDePagamento;
private Long valorTotal;


public Venda() {
}
public Venda(String dataVenda, Categoria categoria, Integer comprador, Integer local,
        Integer formaDePagamento, Long valorTotal) {
    this.dataVenda = dataVenda;
    this.categoria = categoria;
    this.comprador = comprador;
    this.local = local;
    this.formaDePagamento = formaDePagamento;
    this.valorTotal = valorTotal;
}

@Override public int hashCode() { return Objects.hash(categoria, comprador, dataVenda, formaDePagamento, idVenda, local, valorTotal); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Venda other = (Venda) obj; return Objects.equals(categoria, other.categoria) && Objects.equals(comprador, other.comprador) && Objects.equals(dataVenda, other.dataVenda) && Objects.equals(formaDePagamento, other.formaDePagamento) && Objects.equals(idVenda, other.idVenda) && Objects.equals(local, other.local) && Objects.equals(valorTotal, other.valorTotal); } public Integer getIdVenda() { return idVenda; } public void setIdVenda(Integer idVenda) { this.idVenda = idVenda; } public String getDataVenda() { return dataVenda; } public void setDataVenda(String dataVenda) { this.dataVenda = dataVenda; } public Categoria getCategoria() { return categoria; } public void setCategoria(Categoria categoria) { this.categoria = categoria; }

public Integer getComprador() { return comprador; } public void setComprador(Integer comprador) { this.comprador = comprador; } public Integer getLocal() { return local; } public void setLocal(Integer local) { this.local = local; } public Integer getFormaDePagamento() { return formaDePagamento; } public void setFormaDePagamento(Integer formaDePagamento) { this.formaDePagamento = formaDePagamento; } public Long getValorTotal() { return valorTotal; } public void setValorTotal(Long valorTotal) { this.valorTotal = valorTotal; }

@Entity public class Categoria {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idCategoria;
private String descricaoCategoria;

public Categoria() {
    // TODO Auto-generated constructor stub
}

public Categoria(String descricaoCategoria) {
    this.descricaoCategoria = descricaoCategoria;
}


@Override
public int hashCode() {
    return Objects.hash(descricaoCategoria, idCategoria);
}


@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Categoria other = (Categoria) obj;
    return Objects.equals(descricaoCategoria, other.descricaoCategoria)
            && Objects.equals(idCategoria, other.idCategoria);
}


public Integer getIdCategoria() {
    return idCategoria;
}


public void setIdCategoria(Integer idCategoria) {
    this.idCategoria = idCategoria;
}

public String getDescricaoCategoria() { return descricaoCategoria; }

public void setDescricaoCategoria(String descricaoCategoria) { this.descricaoCategoria = descricaoCategoria; }

11 respostas

DTO public class CategoriaDto {

private Integer idCategoria;
private String descricaoCategoria;


public CategoriaDto(Categoria categoria) {
    this.idCategoria = categoria.getIdCategoria();
    this.descricaoCategoria = categoria.getDescricaoCategoria();
}


public Integer getIdCategoria() {
    return idCategoria;
}


public String getDescricaoCategoria() {
    return descricaoCategoria;
}


public static List<CategoriaDto> converter(List<Categoria> categorias) {
    return categorias.stream().map(CategoriaDto::new).collect(Collectors.toList());
}

public class VendaDto {

private Integer idVenda;
private String dataVenda;
private Categoria categoria;
private Integer comprador;
private Integer local;
private Integer formaDePagamento;
private Long valorTotal;

public VendaDto(Venda venda) {
    this.idVenda = venda.getIdVenda();
    this.dataVenda = venda.getDataVenda();
    this.categoria = venda.getCategoria();
    this.comprador = venda.getComprador();
    this.local = venda.getLocal();
    this.formaDePagamento = venda.getFormaDePagamento();
    this.valorTotal = venda.getValorTotal();
}

public Integer getIdVenda() {
    return idVenda;
}

public String getDataVenda() {
    return dataVenda;
}

public Categoria getCategoria() {
    return categoria;
}

public Integer getComprador() {
    return comprador;
}

public Integer getLocal() {
    return local;
}

public Integer getFormaDePagamento() {
    return formaDePagamento;
}

public Long getValorTotal() {
    return valorTotal;
}

public static List<VendaDto> converter(List<Venda> vendas) {
    return vendas.stream().map(VendaDto::new).collect(Collectors.toList());
}

FORM

public class CategoriaForm {

private Integer idCategoria;
private String descricaoCategoria;


public Integer getIdCategoria() {
    return idCategoria;
}
public void setIdCategoria(Integer idCategoria) {
    this.idCategoria = idCategoria;
}
public String getDescricaoCategoria() {
    return descricaoCategoria;
}
public void setDescricaoCategoria(String descricaoCategoria) {
    this.descricaoCategoria = descricaoCategoria;
}

public Categoria converter(CategoriasRepository categoriasRepository) {
    return new Categoria(descricaoCategoria);
}

public class VendaForm {

private String dataVenda = LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"));
private Categoria categoria;
private Integer comprador;
private Integer local;
private Integer formaDePagamento;
private Long valorTotal;

public String getDataVenda() {
    return dataVenda;
}
public void setDataVenda(String dataVenda) {
    this.dataVenda = dataVenda;
}
public Categoria getCategoria() {
    return categoria;
}
public void setCategoria(Categoria categoria) {
    this.categoria = categoria;
}
public Integer getComprador() {
    return comprador;
}
public void setComprador(Integer comprador) {
    this.comprador = comprador;
}
public Integer getLocal() {
    return local;
}
public void setLocal(Integer local) {
    this.local = local;
}
public Integer getFormaDePagamento() {
    return formaDePagamento;
}
public void setFormaDePagamento(Integer formaDePagamento) {
    this.formaDePagamento = formaDePagamento;
}
public Long getValorTotal() {
    return valorTotal;
}
public void setValorTotal(Long valorTotal) {
    this.valorTotal = valorTotal;
}
public Venda converter(VendasRepository vendasRepository) {
    System.out.println(comprador);
    return new Venda(dataVenda, categoria, comprador, local, formaDePagamento, valorTotal);

}

Controller

@RestController @CrossOrigin(origins = "http://localhost:4200", maxAge = 3600) @RequestMapping("/vendas") public class VendaController {

@Autowired
private VendasRepository vendasRepository;

@GetMapping
public List<VendaDto> listarVendas(){
    List<Venda> vendas = vendasRepository.findAll();
    return VendaDto.converter(vendas);
}

@PostMapping
public ResponseEntity<VendaDto> cadastrarVenda(@RequestBody @Valid VendaForm dados, UriComponentsBuilder uriBuilder) {
    Venda venda = dados.converter(vendasRepository);
    vendasRepository.save(venda);

    URI uri = uriBuilder.path("/venda/{id}").buildAndExpand(venda.getIdVenda()).toUri();
    return ResponseEntity.created(uri).body(new VendaDto(venda));
}

}

@RestController @CrossOrigin(origins = "http://localhost:4200", maxAge = 3600) @RequestMapping("/categorias") public class CategoriasController {

@Autowired
private CategoriasRepository categoriasRepository;

@GetMapping
public List<CategoriaDto> listarcategorias() {
    List<Categoria> categorias = categoriasRepository.findAll();
        return CategoriaDto.converter(categorias);
}

@PostMapping
public ResponseEntity<CategoriaDto> cadastrar(@RequestBody @Valid CategoriaForm dados, UriComponentsBuilder uriBuilder) {
    Categoria categoria = dados.converter(categoriasRepository);
    categoriasRepository.save(categoria);

    URI uri = uriBuilder.path("/categoria/{id}").buildAndExpand(categoria.getIdCategoria()).toUri();
    return ResponseEntity.created(uri).body(new CategoriaDto(categoria));
}

Oi Alexandre,

O problema é que na sua classe VendaForm o seu atributo categoria é do tipo Categoria, ou seja, a entidade JPA completa e por isso acontece o problema.

Mas no seu caso deveria ser apenas um Integer representando o id da categoria:

public class VendaForm {

    private String dataVenda = LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"));

    // nao declare entidades JPA como atributo nas classes DTO/FORM
    //private Categoria categoria;
    private Integer categoria;

    private Integer comprador;
    private Integer local;
    private Integer formaDePagamento;
    private Long valorTotal;
}

Confira também nas suas outras classes form e dto se tem atributos que são entidades JPA.

Olá Rodrigo

Tentei fazer esse ajuste na classe VendaForm, porém ao invocar o seguinte método, que está definido no construtor da classe modelo Venda, ele espera o atributo Categoria categoria, causando assim um erro.

public Venda converter(VendasRepository vendasRepository) {
        System.out.println(comprador);
        return new Venda(dataVenda, categoria, comprador, local, formaDePagamento, valorTotal);

    }

O que deve ser feito ? Uma conversão ? Segue abaixo a classe de modelo

@Entity
public class Venda {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer idVenda;
    private String dataVenda;    
    @JsonIgnore
    @ManyToOne
    @JoinColumn(name = "categoria_id")
    private Categoria categoria;
    private Integer comprador;
    private Integer local;
    private Integer formaDePagamento;
    private Long valorTotal;

    public Venda() {
    }
    **public Venda(String dataVenda, Categoria categoria, Integer comprador, Integer local,
            Integer formaDePagamento, Long valorTotal) {
        this.dataVenda = dataVenda;
        this.categoria = categoria;
        this.comprador = comprador;
        this.local = local;
        this.formaDePagamento = formaDePagamento;
        this.valorTotal = valorTotal;
    }**
    @Override
    public int hashCode() {
        return Objects.hash(categoria, comprador, dataVenda, formaDePagamento, idVenda, local, valorTotal);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Venda other = (Venda) obj;
        return Objects.equals(categoria, other.categoria) && Objects.equals(comprador, other.comprador)
                && Objects.equals(dataVenda, other.dataVenda)
                && Objects.equals(formaDePagamento, other.formaDePagamento) && Objects.equals(idVenda, other.idVenda)
                && Objects.equals(local, other.local) && Objects.equals(valorTotal, other.valorTotal);
    }
    public Integer getIdVenda() {
        return idVenda;
    }
    public void setIdVenda(Integer idVenda) {
        this.idVenda = idVenda;
    }
    public String getDataVenda() {
        return dataVenda;
    }
    public void setDataVenda(String dataVenda) {
        this.dataVenda = dataVenda;
    }
    public Categoria getCategoria() {
        return categoria;
    }
    public void setCategoria(Categoria categoria) {
        this.categoria = categoria;
    }
    public Integer getComprador() {
        return comprador;
    }
    public void setComprador(Integer comprador) {
        this.comprador = comprador;
    }
    public Integer getLocal() {
        return local;
    }
    public void setLocal(Integer local) {
        this.local = local;
    }
    public Integer getFormaDePagamento() {
        return formaDePagamento;
    }
    public void setFormaDePagamento(Integer formaDePagamento) {
        this.formaDePagamento = formaDePagamento;
    }
    public Long getValorTotal() {
        return valorTotal;
    }
    public void setValorTotal(Long valorTotal) {
        this.valorTotal = valorTotal;
    }

Oi Alexandre,

Isso mesmo, como só chega o id da entidade, você precisa utilizar o repository para carregar o objeto completo do banco de dados, para ai sim passar como parâmetro no construtor da classe Venda.

Certo

Não consegui compreender muito bem, teria alguma aula ou curso especifico para me recomendar, que fale deste assunto ?

Eu segui baseado nas aulas do cursos Persistência com JPA: Introdução ao Hibernate e ** Spring Boot API REST: Construa uma API, porém no curso mostra o relacionamento entre a entidade **Tópico e Autor, mais não apresenta no curso esse conceito de usar o repository durante a ação do post

poderia me indicar algo ?

solução!

No prórpio curso de Spring Boot tem um exemplo similar, no cadastro de tópico que precisa relacionar com o Curso. O json enviado contem apenas o nome do curso, mas para instanciar o objeto topico é necessário utilizar o repository para cadastrar o curso pelo nome.

https://cursos.alura.com.br/course/spring-boot-api-rest/task/55823

Lá pelo minuto 13:00 é mostrado a lógica de carregar o curso pelo nome. No seu caso seria carregar a categoria pelo id.

Acompanhei o curso realmente apresenta o conceito

Porém o método não funciona, e apresenta erro na linha em negrito (Type mismatch: cannot convert from Optional to Categoria)

public Venda converter(CategoriasRepository categoriasRepository) {    
        **Categoria categoria = categoriasRepository.findById(idCategoria);**
        return new Venda(dataVenda, idCategoria, comprador, local, formaDePagamento, valorTotal);

    }

Fiz o ajuste abaixo

public Venda converter(CategoriasRepository categoriasRepository) {    
        Optional<Categoria> categoria = categoriasRepository.findById(idCategoria)
        return new Venda(dataVenda, idCategoria, comprador, local, formaDePagamento, valorTotal);

    }

em seguida surgiu outro erro que pede a criação de um construtor que receba o argumento do tipo Integer (The constructor Venda(String, Integer, Integer, Integer, Integer, Long) is undefined)

Segue o que temos na classe Venda modelo

public Venda(String dataVenda, Categoria categoria, Integer comprador, Integer local,
            Integer formaDePagamento, Long valorTotal) {
        this.dataVenda = dataVenda;
        this.categoria = categoria;
        this.comprador = comprador;
        this.local = local;
        this.formaDePagamento = formaDePagamento;
        this.valorTotal = valorTotal;
    }

Qual seria a solução ? estou seguindo a nomeclatura da JPA corretamente para o findById funcionar?

Ah sim, troque o findById pelo método getOne e na linha de instanciar o objeto Venda está com problema pois ao invés de passar o objeto categoria você está passando o id:

public Venda converter(CategoriasRepository categoriasRepository) {    
    Categoria categoria = categoriasRepository.getOne(idCategoria);
    return new Venda(dataVenda, categoria, comprador, local, formaDePagamento, valorTotal);
}

Fiz esse ajuste mais esta relatando que o método é depreciado Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Ops, falei errrado. Na verdade utilize o método getById.