Como adicionar atributos com relaciomento de chave estrangeira

Olá, estou tendo problemas ao adicionar atributos através de um formulário. O problema é que ao salvar os dados no front-end os dados vão para suas respectivas tabelas. No caso, são duas. Uma tem que ter a chave primária de uma outra. Mas ao salvar não é possível adicionar a chave estrangeira de uma forma automática. Peço a ajuda de quem souber fazer isso.

O grande problema é que não sei inserir dois atributos em um formulário. Apenas um. Ou seja, ao inserir uma relação de chave estrangeira.

Aqui vai o meu Controller:

@Controller
public class ClienteController {
  
  @Autowired
  private ClienteRepository clienteRepository;
  
  @SuppressWarnings("unused")
  @Autowired
  private PessoaRepository pessoaRepository;
  
  @GetMapping("/formCliente")
  public ModelAndView cadastrarClientes(Pessoa pessoa, Cliente cliente) {
    ModelAndView mv = new ModelAndView();
    mv.setViewName("Cliente/formCliente");
    mv.addObject("cliente",new Cliente());
    mv.addObject("pessoa",new Pessoa()); return mv;
  }
  
  @RequestMapping("/salvarClientes")
  public ModelAndView salvarCliente(
    @Valid Cliente cliente, 
    Pessoa pessoa, 
    Short codigo, 
    BindingResult br
  ) throws Exception {
    ModelAndView mv = new ModelAndView();
    
    if (br.hasErrors()) {
      mv.setViewName("Cliente/formCliente"); mv.addObject("cliente");
      mv.addObject("pessoa");
    } else {
      mv.setViewName("redirect:/clientes-adicionados");
      
      pessoaRepository.save(pessoa);
      clienteRepository.save(cliente);
    }
    
    return mv;
  }
}

Esse é um trexo do meu front-enda onde eu chamo os objetos, Creio que seja nele que esta o o problema.

Acho que é quando chama, ao ultilizar o themeleaf.

Se quiserem que eu poste as classes poderei postar.

Quando o save é invocado, o ID será preenchido pelo JPA. Com isso, vc pode pegar o ID da pessoa e setar no cliente antes de invocar o save do cliente.

Faz o teste ai.

Eu pensei nisso, problema é como implementar isso. Como setar? Pode me orientar?

Tente assim:

// após a execução dessa linha, a pessoa já deve está com o ID preenchido
pessoaRepository.save(pessoa);

cliente.setId(pessoa.getId());
clienteRepository.save(cliente);

Olá amigo, tentei desse modo, mas ele não consegui pegar a chave primaria de pessoa e adicionar na tabela cliente como chave estrangeira. Mesmo com a tabela pessoa ja ter uma chave primaria. Abaixo vai o codigo gerado no console em sql:

Hibernate: 
    insert 
    into
        pessoa
        (bairro, celular, cep, complemento, cpf, email, nome, numero, rg, rua, telefone) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    select
        cliente0_.codigo as codigo1_1_1_,
        cliente0_.data_cadastro as data_cad2_1_1_,
        cliente0_.codigo_pessoa as codigo_p3_1_1_,
        pessoa1_.codigo as codigo1_2_0_,
        pessoa1_.bairro as bairro2_2_0_,
        pessoa1_.celular as celular3_2_0_,
        pessoa1_.cep as cep4_2_0_,
        pessoa1_.complemento as compleme5_2_0_,
        pessoa1_.cpf as cpf6_2_0_,
        pessoa1_.email as email7_2_0_,
        pessoa1_.nome as nome8_2_0_,
        pessoa1_.numero as numero9_2_0_,
        pessoa1_.rg as rg10_2_0_,
        pessoa1_.rua as rua11_2_0_,
        pessoa1_.telefone as telefon12_2_0_ 
    from
        cliente cliente0_ 
    left outer join
        pessoa pessoa1_ 
            on cliente0_.codigo_pessoa=pessoa1_.codigo 
    where
        cliente0_.codigo=?
Hibernate: 
    insert 
    into
        cliente
        (data_cadastro, codigo_pessoa) 
    values
        (?, ?)

Eu quero que contenha o codigo da tabela pessoa no cliente. Para que Haja um relacionamento entre as duas.

image

Ah, entendi. Estava pensando em outra coisa. Vc pode postar as classes Pessoa e Cliente?

Pessoa

@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name="pessoa")
@Entity
public class Pessoa extends GenericDomain{

  @Column(name = "nome")
  @Size(min = 5, max = 35,message = "O nome deve conter no minimo 5 caracteres")
  @NotNull(message = "o campo nome não pode ser nulo")
  private String nome;

  @Column(name = "cpf")
  @Size(max = 14)
  @NotBlank(message ="O cpf não pode ser vazio")
  @NotNull(message = "o campo cpf não pode ser nulo")
  private String cpf;

  @Column(name = "rg")
  @Size(max = 12)
  @NotBlank(message ="O rg não pode ser vazio")
  @NotNull(message = "o campo rg não pode ser nulo")
  private String rg;

  @Column(name = "rua")
  @Size(max = 10)
  @NotBlank(message ="O rua não pode ser vazio")
  @NotNull(message = "o campo rua não pode ser nulo")
  private String rua;

  @Column(name = "numero")
  @NotNull(message = "o campo numero não pode ser nulo")
  private Short numero;

  @Column(name = "bairro")
  @Size(max = 30)
  @NotBlank(message ="O bairro não pode ser vazio")
  @NotNull(message = "o campo bairro não pode ser nulo")
  private String bairro;

  @Column(name = "cep")
  @Size(max = 10)
  @NotBlank(message ="O cep não pode ser vazio")
  @NotNull(message = "o campo cep não pode ser nulo")
  private String cep;

  @Column(name = "complemento")
  @Size(max = 10)
  @NotBlank(message ="O complemento não pode ser vazio")
  @NotNull(message = "o campo complemento não pode ser nulo")
  private String complemento;

  @Column(name = "telefone")
  @Size(max = 13)
  @NotBlank(message ="O telefone não pode ser vazio")
  @NotNull(message = "o campo telefone não pode ser nulo")
  private String telefone;

  @Column(name = "celular")
  @Size(max = 14)
  @NotBlank(message ="O celular não pode ser vazio")
  @NotNull(message = "o campo celular não pode ser nulo")
  private String celular;

  @Column(name = "email")
  @Size(max = 100)
  @NotBlank(message ="O email não pode ser vazio")
  @NotNull(message = "o campo email não pode ser nulo")
  private String email;

  @OneToOne(mappedBy = "pessoa")
  private Cliente cliente;
}

Cliente

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Cliente extends GenericDomain {
  
  @Column
  private String dataCadastro;
  
  @OneToOne(cascade = CascadeType.ALL)
  @JoinColumn(name = "codigo_pessoa")
  private Pessoa pessoa;
}

OBS: Criei uma classe mãe com a chave primaria das entidades. Segue abaixo:

@Data
@MappedSuperclass
public class GenericDomain {
  
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Short codigo;
}

Ah blz, então faça assim na hora de salvar:

cliente.setPessoa(pessoa);
pessoaRepository.save(pessoa);
clienteRepository.save(cliente);
1 curtida

Amigo, muito obrigado. Funcionou!!

image

Você pode me explicar o que você fez? Você relacionou os dois objetos a partir do método set? Ou seja, tinha que haver essa relação? Setando o método set com o parâmetro pessoa?

Do jeito que vc estava fazendo, o Cliente, antes de ser salvo, estava com a propriedade pessoa como NULL. Assim, o JPA não teria como saber o que inserir na coluna de pessoa.

Ao setar a pessoa no cliente, vc coloca a referência do objeto de pessoa no cliente, com isso, o JPA vai saber que, ao inserir a pessoa no banco, que o ID dessa pessoa também deve ser adicionada na tabela cliente. Ele faz isso usando as anotações de relacionamento (@OneToOne) e do id (@Id).