Não Estou Conseguindo Persistir Dados com Spring e Hibernate

Olá,

Estou desenvolvendo uma API que cadastra clientes. Quando eu vou fazer a persistência dos dados com o repository, eu recebo uma exception informando que o obejto que eu estou querendo salvar está null (java.lang.NullPointerException: Cannot invoke “syshotel.com.api.domain.cliente.CLientePjRepository.save(Object)” because “this.cLientePjRepository” is null) . Estou recebendo os dados via JSON pelo insominia, instancio uma variável com os dados recebidos do insominia, já fiz a verificação e constatei que de fato foi instanciado com os dados corretamente, mas mesmo assim não consigo fazer a persistência. Segue abaixo as minhas classes:

Controller

@RestController
@RequestMapping("/clientes")
public class ClienteController {

    @Autowired
    private CadastraCliente clienteService;

    @PostMapping
    @Transactional
    private void cadastrar(@RequestBody DadosCadastroCliente dados) {
        clienteService.cadastrar(dados);
    }


}

PessoaJuridica

@Entity(name = "PessoaJuridica")
@Table(name = "clientes_pj")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class PessoaJuridica extends Cliente  {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@Column(name="razao_social")
	private String razaoSocial;

	@Column(name="nome_fantasia")
	private String nomeFantasia;
	private String cnpj;

	private String email;

	@OneToMany(mappedBy = "pessoaJuridica")
	private List<Endereco> endereco;

	@OneToOne
	private Telefone telefone;

	public PessoaJuridica(String razaoSocial, String nomeFantasia, String cnpj, String email) {
		this.razaoSocial = razaoSocial;
		this.nomeFantasia = nomeFantasia;
		this.cnpj = cnpj;
		this.email = email;
	}
}


Classe Cliente Service (CadastraCliente):

@Service
public class CadastraCliente {
    @Autowired
    private ClientePfRepository clientePfRepository;

    @Autowired
    private ClientePjRepository clientePjRepository;

    @Autowired
    private EnderecoRepository enderecoRepository;

    @Autowired
    private TelefoneRepository telefoneRepository;


    public void cadastrar(DadosCadastroCliente dados){
        if(dados.isJuridico()){
            var clientePj = clientePjRepository.save(new PessoaJuridica(dados.razaoSocial(),
                    dados.nomeFantasia(), dados.cnpj(), dados.email()));

            var endereco = dados.endereco();

            var telefone = dados.telefone();

            for (DadosEndereco item : endereco){
                var enderecoPj = new Endereco(item, null, clientePj);
                enderecoRepository.save(enderecoPj);
            }

            var telefonePj = new Telefone(telefone, null, clientePj);
            telefoneRepository.save(telefonePj);



        } else if(!dados.isJuridico()){

            var clientePf = clientePfRepository.save(new PessoaFisica(dados.nome(), dados.dataNascimento(),
                    dados.cpf(), dados.rg(), dados.sexo(), dados.email()));

            var endereco = dados.endereco();

            var telefone = dados.telefone();

            for (DadosEndereco item : endereco){
                var enderecoPj = new  Endereco(item, clientePf, null);
                enderecoRepository.save(enderecoPj);
            }


            var telefonePj = new Telefone(telefone, clientePf, null);
            telefoneRepository.save(telefonePj);

        }


    }


}

JSON envido pelo insomnia

{
	"isJuridico": true,
	"razaoSocial": "Fábrica de Panelas S/A",
	"nomeFantasia": "Panelas de Ouro",
	"cnpj": "11111111111111111",
	"email": "financeiro@panelasdeouro.com.br",
	"endereco": [
		{
			"cep": "44444444",
			"logradouro": "Av das Panelas",
			"numero": "44",
			"bairro": "Centro",
			"cidade": "Taquarana",
			"estado": "AL"
		},
		{
			"cep": "55555555",
			"logradouro": "Av das Tampas",
			"numero": "77",
			"bairro": "Árvores",
			"cidade": "Taquarana",
			"estado": "AL"			
		}
	]
	
}

Alguém poderia me informar qo que eu estou fazendo de errado?

Pode postar o código de telefone e endereço junto com o stacktrace completo?

Telefone

@Entity(name = "Telefone")
@Table(name = "telefones")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Telefone {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String fixo;
	private String celular;
	private String comercial;

	@ManyToOne
	@JoinColumn(name="pessoafisica_id")
	private PessoaFisica pessoaFisica;

	@ManyToOne
	@JoinColumn(name="pessoajuridica_id")
	private PessoaJuridica pessoaJuridica;


	public Telefone(DadosTelefone dados, PessoaFisica pf, PessoaJuridica pj) {
		this.celular = dados.celular();
		this.comercial = dados.comercial();
		this.fixo = dados.fixo();
		if(pf == null){
			this.pessoaJuridica = pj;
		}
		if (pj == null){
			this.pessoaFisica = pf;
		}

	}
}

Obs.: Fiz uma alteração na classe cliente e telefone, onde o Telefone é mais uma List e sim apenas DadosTelefone, pois não será necessários mais de um registro na tabela telefones para um cliente, será OneToOne.

Endereco

@Entity(name = "Endereco")
@Table(name = "enderecos")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Endereco {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String cep;
	private String logradouro;
	private String numero;
	private String complemento;
	private String bairro;
	private String cidade;
	private String estado;

	@ManyToOne
	@JoinColumn(name="pessoafisica_id")
	private PessoaFisica pessoaFisica;

	@ManyToOne
	@JoinColumn(name="pessoajuridica_id")
	private PessoaJuridica pessoaJuridica;



	public Endereco(DadosEndereco dados, PessoaFisica pf, PessoaJuridica pj) {
		this.cep = dados.cep();
		this.logradouro = dados.logradouro();
		this.numero = dados.numero();
		this.complemento = dados.complemento();
		this.bairro = dados.bairro();
		this.cidade = dados.cidade();
		this.estado = dados.estado();
		if(pf == null){
			this.pessoaJuridica = pj;
		}
		if (pj == null){
			this.pessoaFisica = pf;
		}
	}
}

A Stacktrace mudou porque eu criei uma classe Service (ClienteService) que ficou todo aquele código que verifica se o cliente que esta vindo é PJ ou PF, e na classe ClientController eu injetei a classe ClienteService e chamei o metodo clienteServiceCadastrarCliente(dados). Mas deu o mesmo erro. Veja a stracktrace:

java.lang.NullPointerException: Cannot invoke "syshotel.com.api.domain.cliente.CadastraCliente.cadastraCliente(syshotel.com.api.domain.cliente.DadosCadastroCliente)" because "this.clienteService" is null
	at syshotel.com.api.controller.ClienteController.cadastrar(ClienteController.java:28) ~[classes/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:578) ~[na:na]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-6.0.12.jar:6.0.12]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-6.0.12.jar:6.0.12]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[tomcat-embed-core-10.1.13.jar:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.0.12.jar:6.0.12]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.13.jar:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.12.jar:6.0.12]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.12.jar:6.0.12]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.0.12.jar:6.0.12]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.12.jar:6.0.12]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.0.12.jar:6.0.12]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.12.jar:6.0.12]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.13.jar:10.1.13]
	at java.base/java.lang.Thread.run(Thread.java:1623) ~[na:na]

PS: Editei os post anterior com as alterações que eu diz

Como está organizada a estrutura de pacotes do seu projeto?

post
Segue imagem.

Posta como vc está chamando a serviço/URL no insomnia?

Como você sabe que está correto o recebimento? Consegui debugar? Pelo erro parece que nem está entrando no método chamado.

Na verdade você configurou mas não está sendo injetado pois o erro mostra que está null.

Eu não tenho uma solução mas sugiro remover esses @autowired de ClienteController e CadastraCliente , e defini-los como final fields na classe. Aí criar um único construtor que receba isso como parâmetro e atribua nao objeto.
Se fizer isso seu programa nunca vai rodar quando estiver problema na configuração, o que é melhor pois assim falhará rápido (abordagem fail fast é bom nesse caso).

Possível causa do problema: precisamos ver como está seu spring boot application. Pode ser que não está configurado para que os componentes sejam escaneados e injetados em todos os packages do projeto. Confira se nesse projeto está desligado o automatic componentScan.

No método cadastrar() do ClienteController, eu dei um sysout nos dados recebidos e ele me mostrou no console. Depois eu instanciei um cliente com os mesmo dados e funcionou.

ApiAplication

package syshotel.com.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;



@SpringBootApplication
public class ApiApplication {

	public static void main(String[] args) {
		SpringApplication.run(ApiApplication.class, args);
	}

}

Fiz isso mas não funcionou.

Como está a classe ClienteService?

digite ou cole o código aqui

Habilita os Logs do springboot em modo debug e veja se os seus beans estão sendo criados. Se estiverem sendo, o problema é na configuração que está excluindo seu pacote/classe service.

Esse projeto eh novo ou, já existe outras partes do sistema similares funcionando?

É um projeto novo, já desmanchei e comecei ele do zero pra ver se volta, mas continua dando mesmo problema. Comecei ele do zero, criei só uma classe, uma interface repository, um DTO e um controller, tudo dentro da raiz do pacote e mesmo assim ele não reconhece as injeções. de dependências. Quebrei a cabeça dois dias seguidos… Vou dar uma parada, rever as aulas de criação de projeto e tentar descobrir onde eu estou errando. Obrigado a todos pela ajuda.

Segue minhas ultimas sugestõos que tu encontrarás o problema. O spring é muito bom e estável, e os logs dizem tudo o que está acontecendo.

Cara, acho que eu sou o dev mais nub desse fórum kkkkk. Não sei como verificar esse logs, mas vou dar uma pesquisada olhar, e postar aqui o resultado.

Faz um teste movendo a classe ApiApplication para syshotel.com.api. Do jeito que tá, parece que tá dentro de syshotel.com.api.domain e aí pode ser que não esteja enxergando o pacote controller. Ou então vê se na classe ApiApplication tu informou os pacotes que devem ser lidos usando a annotation @ComponentScan.

Depois de muito quebrar a cabeça e tentar todo tipo de artifício. Resolvi recriar o projeto do zero, com um classe apenas(Hospede) contendo os atributos de endereço e telefone . Apenas separei as classes Endereço e Telefone mas com a anotação @Embedded da JPA. Mas na bases de dados só foi criado a tabela hospedes todos os atributos.

Então, como eu tinha falado, comecei do zero, instalei as dependências apenas do Spring Web, Lombok e Dev Tools. Criei o HospedesController, criei um método cadastrar() com a anotação @PostMapping, testei o envio do json, criei um DTO do recebimento desses dados, testei o recebimento. Aí instalei as dependências do Spring Data, driver do Mysql e Flyway, criei a base de dados, criei a entidade Hospede com as devidas anotações do JPA, criei a interface HospedeRepository, criei a tabela hospedes através da migration e voi a lá… Funcionou. Consegui usar o método save() do repository tranquilamente.

Sou novo no Spring, não sei se existe uma ordem correta para se criar um projeto, mas acredito que o problema estava nesta ordem. Pois no início eu instalei todas as dependências(Spring Web, Spring Data, Fly-way, Driver Mysql) e logo no primeiro start da aplicação ele já reclamou que não existia Migration, então eu tive que criar a migration, para depois criar as entidades jpa. Então eu acredito que o Spring vai “entendendo” o que você está fazendo a medida em que você faz aos poucos e vai testando. Posso estar falando besteira, mas acredito que foi isso.

Mais uma vez agradeço a ajuda de todos.

Basicamente, sim. Spring é “convention over configuration”, em outras palavras, ele é baseado em convenções para reduzir o número de configurações. Então, temos que respeitar as regras/convenções para que ele possa funcionar.