1
resposta

[Dúvida] Como acessar dados de uma classe filha pelo th:value do Thymeleaf?

Criei uma aplicação web em que é possível cadastrar Pessoa Física ou Pessoa Jurídica por um formulário, e as pessoas cadastradas são exibidas numa tabela. Para isso, fiz a classe ClientePessoaFisica e ClientePessoaJuridica extenderem da classe Cliente:

class Cliente:

@Entity
@Table(name = "clientes")
@Inheritance(strategy = InheritanceType.JOINED)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Cliente {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String email;
    private String telefone;
    private String tipoCliente;

    @Embedded
    private Endereco endereco;
    

class ClientePessoaFisica:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class ClientePessoaFisica extends Cliente {
    private String nome;
    private String cpf;
}

class ClientePessoaJuridica:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class ClientePessoaJuridica extends Cliente {

    private String razaoSocial;
    private String nomeFantasia;
    private String cnpj;
}

ClienteRepository:

public interface ClienteRepository extends JpaRepository<Cliente, Long> {
}

Construtor:

   @GetMapping("/atualizaPessoaFisica")
    public String atualizaPessoaFisica(Long id, Model model) {
        var cliente = repository.getReferenceById(id);
        model.addAttribute("cliente", cliente);
        return "clientes/atualizaPessoaFisica";
    }

Ambos os tipos são salvos no ClienteRepository e estão sendo exibidos corretamente na lista, porém ao tentar acessar dados de um id específico pelo repository.getReferenceById(id) usando, por exemplo, th:value="${cliente.nome}" é exibido um erro, pois é recuperado o id da tabela clientes onde nome é nulo:

Uma exemplo de tabela mysql usando o HeidiSQL Como posso fazer para acessar o atributo nome na tabela cliente_pessoa_fisica através do Repository? Eu estou trabalhando com a herança da maneira correta? Agradeço desde já.

1 resposta

Olá, Henrique!

Pelo que percebi, você está usando a estratégia de herança JOINED no Hibernate, que cria uma tabela para cada classe na hierarquia de herança. Nesse caso, a classe base (Cliente) tem uma tabela, e cada subclasse (ClientePessoaFisica e ClientePessoaJuridica) tem sua própria tabela que contém apenas os atributos adicionais. As tabelas são conectadas através de uma chave estrangeira.

O problema que você está enfrentando é que, ao recuperar uma instância de Cliente, o Hibernate só está carregando os dados da tabela Cliente, mas não os dados das tabelas ClientePessoaFisica ou ClientePessoaJuridica. Isso acontece porque a instância recuperada é um proxy, que é uma instância de uma subclasse gerada dinamicamente de Cliente. O Hibernate só carrega os dados da subclasse quando você acessa um de seus atributos.

Uma solução para isso é forçar o Hibernate a carregar os dados da subclasse imediatamente. Você pode fazer isso chamando um método getter da subclasse. Por exemplo, você poderia adicionar o seguinte código ao seu método atualizaPessoaFisica:

    @GetMapping("/atualizaPessoaFisica")
    public String atualizaPessoaFisica(Long id, Model model) {
        var cliente = repository.getReferenceById(id);
        if (cliente instanceof ClientePessoaFisica) {
            var nome = ((ClientePessoaFisica) cliente).getNome();
        }
        model.addAttribute("cliente", cliente);
        return "clientes/atualizaPessoaFisica";
    }

Essa abordagem tem a desvantagem de que você precisa saber qual subclasse está lidando. Se você tiver muitas subclasses, isso pode ser impraticável. Uma abordagem alternativa seria usar a estratégia de herança SINGLE_TABLE, que armazena todas as classes em uma única tabela. Isso simplifica a recuperação de dados, mas pode levar a muitos campos nulos se suas subclasses tiverem muitos atributos exclusivos.

Espero ter ajudado e bons estudos!