1
resposta

Como salvar entidades filhas junto com a pai

Boa tarde, pessoal!

Estou estudando um pouco de API Rest, utilizando Springboot + Mapstruct + JPA e não consigo salvar minhas entidades filhas num único payload

DTO

public class ClienteRequestDto {

    private Long id;
    private String nome;
    private String cpfCnpj;
    private TipoCliente tipoCliente;
    private LocalDate dataNascimentoConstituicao;
    private List<TelefoneRequestDto> telefones;
    private List<EmailRequestDto> emails;
    private List<EnderecoRequestDto> enderecos;
    private List<VeiculoRequestDto> veiculos;

    public Long getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

    public String getCpfCnpj() {
        return cpfCnpj;
    }

    public TipoCliente getTipoCliente() {
        return tipoCliente;
    }

    public LocalDate getDataNascimentoConstituicao() {
        return dataNascimentoConstituicao;
    }

    public List<TelefoneRequestDto> getTelefones() {
        return telefones;
    }

    public List<EmailRequestDto> getEmails() {
        return emails;
    }

    public List<EnderecoRequestDto> getEnderecos() {
        return enderecos;
    }

    public List<VeiculoRequestDto> getVeiculos() {
        return veiculos;
    }
}

Controller

@RestController
public class ClienteController {

    private final ClienteService clienteService;
    private final ClienteMapper clienteMapper;

    public ClienteController(ClienteService clienteService, ClienteMapper clienteMapper) {
        this.clienteService = clienteService;
        this.clienteMapper = clienteMapper;
    }

    @GetMapping("/clientes")
    public List<Cliente> listar() {
        return clienteService.listar();
    }

    @PostMapping("/clientes")
    public ResponseEntity<Cliente> criar(@RequestBody ClienteRequestDto clienteRequestDto) {
        Cliente cliente = clienteMapper.dtoToCliente(clienteRequestDto);
        clienteService.criar(cliente);
        return new ResponseEntity<>(cliente, HttpStatus.CREATED);
    }
}

Service

@Service
public class ClienteService {

    private final ClienteRepository clienteRepository;
    private final ClienteMapper clienteMapper;

    public ClienteService(ClienteRepository clienteRepository, ClienteMapper clienteMapper) {
        this.clienteRepository = clienteRepository;
        this.clienteMapper = clienteMapper;
    }

    public List<Cliente> listar() {
        return clienteRepository.findAll()
                .stream()
                .map(clienteMapper::entityToCliente)
                .toList();
    }

    public Cliente criar(Cliente cliente) {
        clienteRepository.save(clienteMapper.clienteToEntity(cliente));
        return cliente;
    }

}

Repository

@Repository
public interface ClienteRepository extends JpaRepository<ClienteEntity, Long> {
    Optional<ClienteEntity> findByCpfCnpj(String cpfCnpj);
}

Entity

@Entity
@Table(name = "clientes")
public class ClienteEntity {

    @Id
    @GeneratedValue
    private Long id;
    private String nome;
    private String cpfCnpj;
    @Enumerated(EnumType.STRING)
    private TipoCliente tipoCliente;
    private LocalDate dataNascimentoConstituicao;

    @OneToMany(mappedBy = "cliente")
    private List<VeiculoEntity> veiculos;

    @OneToMany(mappedBy = "cliente", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<TelefoneEntity> telefones;

    @OneToMany(mappedBy = "cliente", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<EmailEntity> emails;

    @OneToMany(mappedBy = "cliente", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<EnderecoEntity> enderecos;

    // getters e setters

Todas as classes filhas são identicas a essa:

public class EmailEntity {

    @Id @GeneratedValue private Long id;
    private String endereco;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "cliente_id")
    private ClienteEntity cliente;

    //GETTERS E SETTERS
}

Quando mando o POST ele salva somente a entidade pai e pula os ids na proxima chamada post:

Resultado

[
    {
        "id": 1,
        "nome": "Renan Padilha",
        "cpfCnpj": "000000000",
        "tipoCliente": "FISICA",
        "dataNascimentoConstituicao": "1924-04-27",
        "telefones": [],
        "emails": [],
        "enderecos": [],
        "veiculos": []
    },
    {
        "id": 6,
        "nome": "Renan",
        "cpfCnpj": "000000000",
        "tipoCliente": "FISICA",
        "dataNascimentoConstituicao": "1924-04-27",
        "telefones": [],
        "emails": [],
        "enderecos": [],
        "veiculos": []
    }
]
1 resposta

Olá, Renan! Como vai?

Aparentemente, o problema está relacionado ao fato de que as entidades filhas não estão sendo corretamente associadas à entidade pai antes de serem salvas.

Pode ser algum problema na lógica do código clienteMapper.clienteToEntity, poderia compartilhar ele aqui para analisarmos melhor?

Lá no seu método criar você pode percorrer a lista de entidades filhas e definir a entidade pai antes de salvá-las também. Por exemplo:

public Cliente criar(Cliente cliente) {
    for (TelefoneEntity telefone : cliente.getTelefones()) {
        telefone.setCliente(cliente);
    }
    
    for (EmailEntity email : cliente.getEmails()) {
        email.setCliente(cliente);
    }
    
    for (EnderecoEntity endereco : cliente.getEnderecos()) {
        endereco.setCliente(cliente);
    }
    
    for (VeiculoEntity veiculo : cliente.getVeiculos()) {
        veiculo.setCliente(cliente);
    }
    
    clienteRepository.save(clienteMapper.clienteToEntity(cliente));
    return cliente;
}

Dessa forma, você estará associando corretamente as entidades filhas à entidade pai antes de salvá-las, garantindo que os ids sejam corretamente preenchidos.

Espero que essa solução te ajude a resolver o problema. Se tiver mais alguma dúvida, é só perguntar!

Espero ter ajudado e bons estudos!