Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

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! :)