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

Relacao ManyToOne

Ola a todos, eu estou com um projetinho e estou travado em uma questão, eu tenho minha classe principal que seria o usuário, e tenho tbm a classe de contas banco, eu estou relacionando a conta que vai ser cadastrada com o usuário, pelo ID dele, minha dúvida é como que posso estar passando pela minha requisição esse meu id, para que meu codigo entenda e relacione meu usuario com a conta, segue codigo e requisição que estou usando:

Model Usuario

package com.example.controlefamiliabackend.models;

import lombok.Data;

import javax.persistence.*;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.Date;


@Data
@Entity
@Table(name = "usuario")
public class UsuarioModel{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_usuario")
    private BigInteger id;

    @Column(name= "email_usuario", nullable = false, unique = true, length = 50)
    private String dsEmail;

    @Column(name= "senha", nullable = false, length = 25)
    private String dsSenha;

    @Column(name= "telefone", length = 25)
    private String dsTelefone;

    @Column(name= "nome", nullable = false, length = 255)
    private String dsNome;

    @Column(name= "dt_nascimento", nullable = false)
    private Date dtNascimento;

    @Column(name= "cpf", length = 25)
    private String dsCpf;

    @Column(name= "endereco", nullable = false, length = 25)
    private String dsEndereco;

    @Column(name= "dt_cadastro", nullable = false)
    private LocalDateTime dtCadastro;

}

Model ContaBanco

package com.example.controlefamiliabackend.models;

import lombok.Data;

import javax.persistence.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;

@Data
@Entity
@Table(name = "conta_bancaria")
public class ContaBancoModel {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private BigInteger idContaBancaria;

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "id_usuario")
    private UsuarioModel titular;

    @Column(nullable = false, length = 250)
    private String codigoBanco;

    @Column(nullable = false, length = 250)
    private String agencia;

    @Column(nullable = false, length = 250)
    private String numConta;

    @Column(nullable = false)
    private BigDecimal saldo;

    @Column(nullable = false)
    private String tipoConta;

    @Column
    private LocalDateTime dtCadastro;
}

requicao de cadastro

{                                                                                           
    "titular":{
        "id": 1
    },
    "codigoBanco": "748",
    "agencia": "0740",
    "numConta": "123456",
    "saldo": 1500,
    "tipoConta": "CC"
}

Agradeço desde já.

11 respostas

Oi Marcos,

A requisição é desse jeito mesmo que você mandou, passando o titular como um objeto json e dentro dele o id. Manda aqui o código da classe DTO que você recebe no controller.

Esse é o DTO que estou usando

package com.example.controlefamiliabackend.dtos;

import com.example.controlefamiliabackend.models.UsuarioModel;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

@Data
public class ContaBancoDto {

    @NotBlank
    private UsuarioModel titular;

    @NotBlank
    private String codigoBanco;

    @NotBlank
    private String agencia;

    @NotBlank
    private String numConta;

    @NotNull
    private BigDecimal saldo;

    @NotBlank
    private String tipoConta;
}

Mas quando mando minha requisição ele esta me retornando um 500 com a seguinte descrição, pelo o que entendi o tipo que estou passando o meu titular não é válido

2022-07-23 12:30:20.358 ERROR 5698 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.NotBlank' validating type 'com.example.controlefamiliabackend.models.UsuarioModel'. Check configuration for 'titular'] with root cause

Está certinho, sendo que o problema é porque não pode utilizar a anotação @NotBlank em atributos que não sejam String:

@NotBlank
private UsuarioModel titular;

Troque para @NotNull que vai funcionar.

Show de bola deu certo, mas agora me estourou outro erro

2022-07-24 05:02:08.692 ERROR 18530 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.example.controlefamiliabackend.models.UsuarioModel; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.controlefamiliabackend.models.UsuarioModel] with root cause

Pelo oq eu andei lendo e por conta do que estou gerando para as contas que estao sendo criadas mas nao sei como proceder nesse caso, se devo alterar alguma anotacao

@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private BigInteger idContaBancaria;

Oi Marcos,

O problema agora é que você está recebendo o objeto UsuarioModel com o id setado na requisição, mas o correto seria receber apenas o id do usuario e fazer um find no banco de dados para carregar o objeto completo. Similar ao que foi feito durante as aulas, no qual eu enviei apenas o nome do curso, carreguei no banco de dados e setei o curso no tópico.

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

Criei a converter e agora estou passando apenas o ID, mas me bateu um novo erro que fiqeui travado novamente, pelas pesquisas que fiz esta faltando alguma anotacao, mas nao consegui descobrir ainda qual seria

2022-07-26 00:18:26.464 ERROR 19802 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.example.controlefamiliabackend.models.ContaBancoModel["titular"]->com.example.controlefamiliabackend.models.UsuarioModel$HibernateProxy$iXMGNJH3["hibernateLazyInitializer"])] with root cause

Oi Marcos,

Posta aqui como ficou seu controller e seu DTO.

Controller

package com.example.controlefamiliabackend.controllers;


import com.example.controlefamiliabackend.dtos.ContaBancoDto;
import com.example.controlefamiliabackend.models.ContaBancoModel;
import com.example.controlefamiliabackend.repositories.UsuarioRepository;
import com.example.controlefamiliabackend.services.ContaBancoService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;

@RestController
@RequestMapping(path = "/conta-banco")
public class ContaBancoController {

    final ContaBancoService contaBancoService;

    @Autowired
    final UsuarioRepository usuarioRepository;

    public ContaBancoController(ContaBancoService contaBancoService, UsuarioRepository usuarioRepository){this.contaBancoService = contaBancoService;
        this.usuarioRepository = usuarioRepository;
    }

    @ResponseBody
    @PostMapping
    public ResponseEntity<Object> saveContaBanco(@RequestBody @Valid ContaBancoDto contaBancoDto){
        if(contaBancoService.verificaBanco(contaBancoDto.getCodigoBanco()) == null){
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Banco nao encontrado");
        }
        ContaBancoModel contaBancoModel = contaBancoDto.converter(usuarioRepository);
        BeanUtils.copyProperties(contaBancoDto, contaBancoModel);
//        contaBancoModel.setDtCadastro(LocalDateTime.now(ZoneId.of("UTF")));
        return ResponseEntity.status(HttpStatus.CREATED).body(contaBancoService.save(contaBancoModel));
    }

    @GetMapping
    public ResponseEntity<List<ContaBancoModel>> getAllContaBnco(){
        return ResponseEntity.status(HttpStatus.OK).body(contaBancoService.findAll());
    }
}

DTO

package com.example.controlefamiliabackend.dtos;

import com.example.controlefamiliabackend.models.ContaBancoModel;
import com.example.controlefamiliabackend.models.UsuarioModel;
import com.example.controlefamiliabackend.repositories.UsuarioRepository;
import lombok.Data;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Optional;

@Data
public class ContaBancoDto {

    @NotNull
    private Integer idUsuario;

    @NotBlank
    private String codigoBanco;

    @NotBlank
    private String agencia;

    @NotBlank
    private String numConta;

    @NotNull
    private BigDecimal saldo;

    @NotBlank
    private String tipoConta;

    public ContaBancoModel converter(UsuarioRepository usuarioRepository){
       UsuarioModel titular = usuarioRepository.getReferenceById(idUsuario);
        return new ContaBancoModel(titular, codigoBanco, agencia, numConta, saldo, tipoConta);
    }
}
solução!

O problema agora está no objeto que você está retornando do controller para o frontend:

return ResponseEntity.status(HttpStatus.CREATED).body(contaBancoService.save(contaBancoModel));

O que esse seu método save da classe contaBancoService está retornando?

Vou chutar que está devolvendo um objeto do tipo ContaBancoModel, que é uma entidade JPA e pode ter algum relacionamento que não foi carregado e com isso gera a exception.

Por isso no curso foi orientado a não receber e nem devolver entidades JPA nos controllers e sim utilizar classes DTO, para evitar esse e outros problemas de acontecer.

A sugestão é você utilizar para um DTO e o devolver no controller, contendo nele apenas os dados que você deseja incluir no json de resposta da API

Peço desculpas pela demora de responder, revi todo o curso e refatorei todo o meu projeto, eu acho que estava bêbado quando iniciei ele kkkkkkk, agora está tudo funcionando, e estou implementando novas funcionalidades e tudo está correndo bem, muito obrigado por sanar minhas dúvidas foi de uma ajuda gigantesca.

Opa, show de bola Marcos!

Bons estudos! :)