[RESOLVIDO] Erro ao consumir API usando o Resttemplate

Boa tarde pessoal, sou iniciante na programação java e estou tentando consumir uma api simples que eu mesmo criei utilizando o resttemplate do spring, a api em questão seria só um serviço para buscar no banco de dados um campo de ano_nascimento, caso a busca retorne algum ano de nascimento igual ao que for passado eu quero que seja retornado apenas a idade calculada com base no ano encontrado, caso não ache nada retornar apenas um 0.

Esse é o código do controller

    @GetMapping("/ano/{anoNascimento}")
    	public String buscaAno(@PathVariable String anoNascimento) {
    		
    		clienteRepository.findByAnoNascimento(anoNascimento);
    		
    		if(clienteRepository.findByAnoNascimento(anoNascimento).isEmpty()==true) {
    			return "0";
    		} else {
    			return anoNascimento ;
    		}		
    	}

Esse é o código do serviço:

    public static void buscaPeloAno() {
    		Map<String, String> param = new HashMap<>();
    		param.put("anoNascimento", "2005");
    		System.out.println(param.put("anoNascimento", "1995"));

    		Cliente cliente = restTemplate.getForObject(BUSCA_PELO_ANO, Cliente.class, param);
    		System.out.println(cliente);
    		
    		if(cliente.getAnoNascimento()=="0") {
    			System.out.println("Ano não encontrado no banco de dados");
    		} else {
    			Integer ano = Integer.parseInt(cliente.getAnoNascimento());
    			Integer anoAtual = LocalDate.now().getYear();
    			Integer resultado = anoAtual - ano;
    			String resultadoString = resultado.toString();
    			System.out.println("A idade é: "+resultadoString);
    			
    		}
    	}

E esse é o erro que me retorna

Error while extracting response for type [class br.com.santarosa.clinica.model.Cliente] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `br.com.santarosa.clinica.model.Cliente` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1995); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `br.com.santarosa.clinica.model.Cliente` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1995)

Como é o json que o serviço retorna? E posta tb a classe Cliente.

Essa é a classe Cliente

package br.com.santarosa.clinica.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "cliente_silas")
public class Cliente {
	@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	@Column(name = "nome")
	private String nome;
	
	@Column(name = "cpf")
	private String cpf;
	
	@Column(name = "cnpj")
	private String cnpj;
	
	@Column(name = "ano_nascimento")
	private String anoNascimento;
	
	@Column(name = "telefone")
	private String telefone;

	public Cliente() {

	}
	
	public Cliente(String nome, String cpf, String cnpj, String anoNascimento, String telefone) {
		this.nome = nome;
		this.cpf = cpf;
		this.cnpj = cnpj;
		this.anoNascimento = anoNascimento;
		this.telefone = telefone;
	}

	

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public String getCpf() {
		return cpf;
	}

	public void setCpf(String cpf) {
		this.cpf = cpf;
	}

	public String getCnpj() {
		return cnpj;
	}

	public void setCnpj(String cnpj) {
		this.cnpj = cnpj;
	}

	public String getAnoNascimento() {
		return anoNascimento;
	}

	public void setAnoNascimento(String anoNascimento) {
		this.anoNascimento = anoNascimento;
	}

	public String getTelefone() {
		return telefone;
	}

	public void setTelefone(String telefone) {
		this.telefone = telefone;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((anoNascimento == null) ? 0 : anoNascimento.hashCode());
		result = prime * result + ((cnpj == null) ? 0 : cnpj.hashCode());
		result = prime * result + ((cpf == null) ? 0 : cpf.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((nome == null) ? 0 : nome.hashCode());
		result = prime * result + ((telefone == null) ? 0 : telefone.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Cliente other = (Cliente) obj;
		if (anoNascimento == null) {
			if (other.anoNascimento != null)
				return false;
		} else if (!anoNascimento.equals(other.anoNascimento))
			return false;
		if (cnpj == null) {
			if (other.cnpj != null)
				return false;
		} else if (!cnpj.equals(other.cnpj))
			return false;
		if (cpf == null) {
			if (other.cpf != null)
				return false;
		} else if (!cpf.equals(other.cpf))
			return false;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (nome == null) {
			if (other.nome != null)
				return false;
		} else if (!nome.equals(other.nome))
			return false;
		if (telefone == null) {
			if (other.telefone != null)
				return false;
		} else if (!telefone.equals(other.telefone))
			return false;
		return true;
	}
}

Sobre o retorno é que eu tenho duvida, se daria pra retornar só um json com esse valor calculado da idade, se tiver como eu faço isso?

Soh para entender melhor. Essa linha:

Cliente cliente = restTemplate.getForObject(BUSCA_PELO_ANO, Cliente.class, param);

Está chamando esse endpoint:

@GetMapping("/ano/{anoNascimento}")
	public String buscaAno(@PathVariable String anoNascimento) {

É isso?

Isso mesmo, defini o end point assim

private static final String BUSCA_PELO_ANO = "http://localhost:8080/clientes/ano/{anoNascimento}";

E esse é o ClienteRepository que eu esqueci de mandar também

package br.com.santarosa.clinica.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import br.com.santarosa.clinica.model.Cliente;

@Repository
public interface ClienteRepository extends JpaRepository<Cliente, Integer>{

	List<Cliente> findByAnoNascimento(String anoNascimento);
	
}

Entendi. Então o erro é pq o endpoint retorna o “anoNascimento” e vc está tentando recuperar como se fosse um “Cliente”.

Então tava querendo pegar o “Cliente” para poder pegar o anoNascimento dele. Como eu deveria fazer para pegar so o anoNascimento que trouxer do banco?

Mas o endpoint “/clientes/ano/{anoNascimento}” está retornando somente uma string com a data de nascimento, e não cliente completo:

@GetMapping("/ano/{anoNascimento}")
	public String buscaAno(@PathVariable String anoNascimento) {
		
		clienteRepository.findByAnoNascimento(anoNascimento);
		
		if(clienteRepository.findByAnoNascimento(anoNascimento).isEmpty()==true) {
			return "0";
		} else {
			return anoNascimento ;
		}		
	}

Entendi, tenho que mudar o retorno do meu endpoint para um Cliente completo então certo.

1 curtida

Isso mesmo!

No erro, a parte:

Cannot construct instance of `br.com.santarosa.clinica.model.Cliente` (although at least one Creator exists): no int/Int-argument constructor/factory method to deserialize from Number value (1995);

Diz que não foi possível encontrar uma forma de construir a classe Cliente para o número de valor 1995.

Entendi mano, inclusive fiz aqui e deu certo quando ele encontra o ano no banco de dados ele me retorna o calculo da idade certo, mas quando eu passo um ano que não existe no banco de dados ele me da um erro, como eu faço esse tratamento pra ele me retornar 0 ao inves de erro?

alterei o método de busca, o 2005 ali é um ano inexistente no banco de dados

public static void buscaPeloAno() {
		Map<String, String> param = new HashMap<>();
		param.put("anoNascimento", "2005");
		Cliente cliente = restTemplate.getForObject(BUSCA_PELO_ANO, Cliente.class, param);
		
		if(cliente.getAnoNascimento().isEmpty()==true) {
			System.out.println("Ano não encontrado no banco de dados");
		} else {
			Integer ano = Integer.parseInt(cliente.getAnoNascimento());
			Integer anoAtual = LocalDate.now().getYear();
			Integer resultado = anoAtual - ano;
			String resultadoString = resultado.toString();
			System.out.println("A idade é: "+resultadoString);
			
		}
	}

Esse é o erro que me retorna com um ano inexistente no banco de dados.

Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.NullPointerException
	at br.com.santarosa.clinica.service.ClienteService.buscaPeloAno(ClienteService.java:85)
	at br.com.santarosa.clinica.ClinicaApplication.main(ClinicaApplication.java:19)
	... 5 more

Talvez o endpoint esteja retornando vazio qdo vc passa um ano inexistente. Com isso, o cliente deve ficar NULL e talvez esteja estourando null pointer nessa linha:

if(cliente.getAnoNascimento().isEmpty()==true) {
      ^
      +-> Se o cliente for NULL, vai dá nullpointer aqui

Vc deve verificar se o cliente é NULL ou não, e fazer algum tratamento.

Entendi Lucas, muuuuito obrigado pela ajuda mesmo, vou tentar fazer esse tratamento aqui, obrigado.

1 curtida