Como retornar somente um campo usando JPQL no Spring Boot?

Do jeito que se encontra o código abaixo ele retorna todos os campos da entidade.

@Component
public class NumeroVotosMembroRepositoryImpl implements NumeroVotosMembroRepositoryQuery{

@PersistenceContext
private EntityManager manager;

@Override
public List<NumeroVotosMembro> ListarNumeros() {
	TypedQuery<NumeroVotosMembro>  consulta = manager.createQuery("FROM NumeroVotosMembro", NumeroVotosMembro.class);
	return consulta.getResultList();
}

Porém eu preciso somente retorna somente um campo da entidade, tentei dessa forma mas gerou um erro e não entendo como corrigir. Preciso de ajuda.

@Override
public List<NumeroVotosMembro> ListarNumeros() {
	TypedQuery<NumeroVotosMembro>  consulta = manager.createQuery("SELECT nome FROM NumeroVotosMembro", NumeroVotosMembro.class);
	return consulta.getResultList();
}

Tente assim:

TypedQuery<Tuple> query = em.createQuery(
  "SELECT nome FROM NumeroVotosMembro", 
  Tuple.class
);

List<Tuple> result = query.getResultList();

E vc consegue pegar o valor da tupla usando get(0) (passando o índice da coluna), ou get(“alias”) (passando o alias da coluna).

Javadoc: https://docs.oracle.com/javaee/7/api/javax/persistence/Tuple.html

2 curtidas

Oi @Lucas_Camara eu tentei, mas não deu certo, essa foi minha tentativa

@Override
public List<NumeroVotosMembro> ListarNumeros() {
	TypedQuery<NumeroVotosMembro>  consulta = manager.createQuery("select nvm.nome FROM NumeroVotosMembro nvm", NumeroVotosMembro.class);
	List<NumeroVotosMembro> resultado = consulta.getResultList();
	return resultado;
}

Tente fazer de forma como postei (usando Tuple) que deve funcionar.

Olá Lucas, agora eu entendi o que você quis dizer. O Tuple me parece um recurso do Java muito interessante. E nesse recurso existe uma serie utilitários que para mim são desconhecidos. Eu tentei pesquisar no YOUTUBE e não encontrei nada muito solido para conseguir entender bem como mexer. Isso está acontecendo porque eu faço parte daqueles programadores que aprenderam tudo com vídeo aulas invés de desenvolver o âmbito de aprender com a própria documentação do Java. Collections e outros conhecimento avançados estavam na minha lista de prioridade de coisas que preciso estudar para o Java, mas em fim meu código ficou desse jeito.

Controller.

@RestController
@RequestMapping("/numero_votos")
public class NumeroVotosMembroResource {
	
	@Autowired
	private NumeroVotosMembroRepository numeroVotosMembroRepository;
	
	@GetMapping
	public List<Tuple> listar() {
		return numeroVotosMembroRepository.ListarNumeros();
	}
}

Implementação em questão

@Repository
public class NumeroVotosMembroRepositoryImpl implements NumeroVotosMembroRepositoryQuery{
	
	@PersistenceContext
	private EntityManager manager;

	@Override
	public List<Tuple> ListarNumeros() {
		TypedQuery<Tuple>  consulta = manager.createQuery("select nvm.nome FROM NumeroVotosMembro nvm", Tuple.class);
		List<Tuple> resultado = consulta.getResultList();
		return resultado;
	}

}	

O resultado no postman foi essa:

[

    {

        "elements": [

            {

                "position": 0,

                "alias": null,

                "hibernateType": {

                    "sqlTypeDescriptor": {

                        "sqlType": 12

                    },

                    "javaTypeDescriptor": {

                        "mutabilityPlan": {

                            "mutable": false

                        },

                        "comparator": {},

                        "javaType": "java.lang.String",

                        "javaTypeClass": "java.lang.String"

                    },

                    "name": "string",

                    "collectionType": false,

                    "anyType": false,

                    "returnedClass": "java.lang.String",

                    "entityType": false,

                    "componentType": false,

                    "associationType": false,

                    "mutable": false,

                    "registrationKeys": [

                        "string",

                        "java.lang.String"

                    ],

                    "xmlelement": false

                },

                "javaType": "java.lang.String"

            }

        ]

    },

    {

        "elements": [

            {

                "position": 0,

                "alias": null,

                "hibernateType": {

                    "sqlTypeDescriptor": {

                        "sqlType": 12

                    },

                    "javaTypeDescriptor": {

                        "mutabilityPlan": {

                            "mutable": false

                        },

                        "comparator": {},

                        "javaType": "java.lang.String",

                        "javaTypeClass": "java.lang.String"

                    },

                    "name": "string",

                    "collectionType": false,

                    "anyType": false,

                    "returnedClass": "java.lang.String",

                    "entityType": false,

                    "componentType": false,

                    "associationType": false,

                    "mutable": false,

                    "registrationKeys": [

                        "string",

                        "java.lang.String"

                    ],

                    "xmlelement": false

                },

                "javaType": "java.lang.String"

            }

        ]

    },

    {

        "elements": [

            {

                "position": 0,

                "alias": null,

                "hibernateType": {

                    "sqlTypeDescriptor": {

                        "sqlType": 12

                    },

                    "javaTypeDescriptor": {

                        "mutabilityPlan": {

                            "mutable": false

                        },

                        "comparator": {},

                        "javaType": "java.lang.String",

                        "javaTypeClass": "java.lang.String"

                    },

                    "name": "string",

                    "collectionType": false,

                    "anyType": false,

                    "returnedClass": "java.lang.String",

                    "entityType": false,

                    "componentType": false,

                    "associationType": false,

                    "mutable": false,

                    "registrationKeys": [

                        "string",

                        "java.lang.String"

                    ],

                    "xmlelement": false

                },

                "javaType": "java.lang.String"

            }

        ]

    }

]

Só tinham mesmo três registros no banco de dados. Eu agradeço muito pela sua contribuição, se não fosse por você nunca teria conhecimento desse recurso do Java e vou estudar mais. :slightly_smiling_face: Mas ainda não conseguir resolver meu problema.:no_mouth:

Vc quase acertou:

@Override
public List<NumeroVotosMembro> ListarNumeros() {
  TypedQuery<Tuple> consulta = manager.createQuery("select nvm.nome FROM NumeroVotosMembro nvm", Tuple.class);
  List<Tuple> resultado = consulta.getResultList();

  return resultado.stream()
    .map(tuple -> new NumeroVotosMembro(tuple.get("nome", String.class)))
    .collect(Collectors.toList());
}

No exemplo, estou imaginando que a classe NumeroVotosMembro possui um construtor que receba uma String

Opa, muito obrigado :heart_eyes: Estou quase chegando lá!! Copiei e colei o código que você me passou no projeto e ele deu esse resultado.

java.lang.IllegalArgumentException: Unknown alias [nome]
	at org.hibernate.jpa.spi.TupleBuilderTransformer$HqlTupleImpl.get(TupleBuilderTransformer.java:126) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
	at org.hibernate.jpa.spi.TupleBuilderTransformer$HqlTupleImpl.get(TupleBuilderTransformer.java:106) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
	at com.nomeacao.repository.voto.NumeroVotosMembroRepositoryImpl.lambda$0(NumeroVotosMembroRepositoryImpl.java:27) ~[classes/:na]
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]

Essa é minha entidade.

@Entity
@Table(name = "numero_votos_membro")
public class NumeroVotosMembro implements Serializable{

	private static final long serialVersionUID = 1L;

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

	private String nome;

	private Integer valor_voto;

	public NumeroVotosMembro() {
	}

	public NumeroVotosMembro(String nome) {
		this.nome = nome;
	}

	public NumeroVotosMembro(Long codigo, String nome, Integer valor_voto) {
		this.codigo = codigo;
		this.nome = nome;
		this.valor_voto = valor_voto;
	}

/* getts e setts */

Eu tentei fazer dessa forma;

	@Override
	public List<NumeroVotosMembro> ListarNumeros() {
	  TypedQuery<Tuple> consulta = manager.createQuery("select nvm.nome FROM NumeroVotosMembro nvm", Tuple.class);
	  List<Tuple> resultado = consulta.getResultList();

	  return resultado.stream()
			  .map(tuple -> new NumeroVotosMembro())
	    .collect(Collectors.toList());
	}

E retornou isso no postman.

[

    {

        "codigo": null,

        "nome": null,

        "valor_voto": null

    },

    {

        "codigo": null,

        "nome": null,

        "valor_voto": null

    },

    {

        "codigo": null,

        "nome": null,

        "valor_voto": null

    }

]

Eu tentei dessa forma também.

  return resultado.stream()
			  .map(tuple -> new NumeroVotosMembro(tuple.get(1, String.class)))
	    .collect(Collectors.toList());

Mas gerou esse erro;

java.lang.IllegalArgumentException: requested tuple index exceeds actual tuple size
	at org.hibernate.jpa.spi.TupleBuilderTransformer$HqlTupleImpl.get(TupleBuilderTransformer.java:153) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
	at org.hibernate.jpa.spi.TupleBuilderTransformer$HqlTupleImpl.get(TupleBuilderTransformer.java:133) ~[hibernate-core-5.4.31.Final.jar:5.4.31.Final]
	at com.nomeacao.repository.voto.NumeroVotosMembroRepositoryImpl.lambda$0(NumeroVotosMembroRepositoryImpl.java:27) ~[classes/:na]
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]

Falta muito pouco para corrigir, estou investigando! :upside_down_face:

Na verdade, eu que passei batido. para recuperar o valor pelo alias, tem que colocar o alias na consulta:

SELECT nvm.nome AS nome FROM NumeroVotosMembro nvm

Ou recupera pelo índice 0 em vez de 1:

tuple.get(0, String.class)
1 curtida

O segundo parâmetro do método createQuery indica qual o tipo de dado que será retornado.

Quando você faz o SELECT pela entidade inteira, aí funciona, afinal você indicou que o retorno da query é NumeroVotosMembro.

Mas quando a query só retorna um dos campos, aí o tipo dela não é mais NumeroVotosMembro. Em vez disso, o retorno será apenas o campo, portanto o tipo deve ser o mesmo do campo sendo retornado.

Por exemplo, se o nome for uma String, basta mudar a query para retornar este tipo:

// troque NumeroVotosMembro por String
@Override
public List<String> ListarNumeros() {
    TypedQuery<String> consulta = manager.createQuery("select nvm.nome FROM NumeroVotosMembro nvm", String.class);
    return consulta.getResultList();
}

Obs: talvez o nome do método devesse ser listarNomes?


De forma geral, quando você não quer todos os campos da entidade, outra opção é retornar cada registro como um array de Object:

// selecionando o ID e NOME
TypedQuery<Object[]> consulta = manager.createQuery("select nvm.id, nvm.nome FROM NumeroVotosMembro nvm", Object[].class);
List<Object[]> resultados = consulta.getResultList();
for (Object[] linha : resultados) {
    System.out.println("id=" + linha[0] + ", nome=" + linha[1]);
}

Ou então você pode criar uma classe customizada que só contém os campos que você quer:

public class CustomObject {
    private int id;
    private String nome;
    public CustomObject(int id, String nome) {
        this.id = id;
        this.nome = nome;
    }
    // getters, etc...
}

// e na query você retorna o CustomObject
TypedQuery<CustomObject> consulta = manager.createQuery("select NEW CustomObject(nvm.id, nvm.nome) FROM NumeroVotosMembro nvm", CustomObject.class);
List<CustomObject> resultados = consulta.getResultList();

Mas no seu caso, como só tem um campo, a primeira opção acima é mais simples.

2 curtidas

Todos os exemplo que você me passou funcionaram. Muito obrigado pela por sua ajuda. Eu sou muito grato por você e o @Lucas_Camara. Todos vocês ajudaram muito, sou muito grato. Eu comprei um curso de JPQL na internet e com ajuda de vocês poderei evoluir no curso que comprei por causa dessas dicas. :smiling_face_with_three_hearts::innocent::partying_face: