Retorno de Consulta SQL nativa em JPA 2.2 sem entidade real

Olá, utilizo JPA 2.2 com Hibernate e tenho todas as minhas tabelas mapeadas em Classes de Entidades com anotações JPA e consigo tranquilamente realizar consultas com JPQL conforme código abaixo:

TypedQuery<TabelaA> query = entityManager.createQuery(jpql, TabelaA.class);

Agora tenho uma duvida na seguinte situação:
Preciso realizar uma consulta em diversas tabelas cujo o retorno não representa nenhuma tabela real no meu banco de dados, ou seja, não possuo uma classe de entidade para realizar a devida query JPQL.

Então pensei em usar uma Query Nativa conforme código abaixo:

String sql = "select a.c1, a.c2, b.c2, c.c1, c.c2, d.c1 ... from tabela_a a join tabela_b b ...<código omitido, pois é muito grande> ....";

Query query = entityManager.createNativeQuery(sql);
List resultQuery = query.getResultList();

O código executa perfeitamente, porém me retorna uma lista com um array de objetos List<Object[]>.

Sei que posso pegar as linhas e colunas com o for, mas gostaria de saber como criar uma classe que não é uma entidade, pensei em uma classe ConsultaDTO com todos os campos de retorno da consulta, e então fazer com que o retorno .getResultList() seja um List<ClasseDTO> e não um List<Object[]>.

Mas não sei como fazer, aguem poderia me ajudar com alguns exemplos, até mesmo outra solução para este problema, pois o que eu preciso é retornar os registros de uma consulta e repassar para o Controller realizar as devidas manipulações e então o resultado destas manipulações que serão salvas no DB normalmente em uma tabela real.

Usando SQL nativo pelo que eu conheço tu tem que montar o mapeamento na mão, se utilizar JPQL aí é possível construir a Query passando a DTO pra ser preenchida.

Tenta assim:

Classe que irá receber os dados

@SqlResultSetMapping(
    name = "mappingTabelaA",
    classes = @ConstructorResult(
        targetClass = TabelaA.class,
        columns = {
            @ColumnResult(name = "campo1", type = Long.class),
            @ColumnResult(name = "campo2", type = String.class),
        }
    )
)
@MappedSuperclass
public class TabelaA {
	private Long campo1;
	private String campo2;
	
	public TabelaA(Long campo1, String campo2) {
		this.campo1 = campo1;
		this.campo2 = campo2;
	}
	
	// getters (não crie setters nessa classe)
}

Consulta

List<TabelaA> resultado = entityManager.createNativeQuery(
	"SELECT a.c1 AS campo1, a.c2 AS campo2 FROM tabela_a a", 
	"mappingTabelaA" // aqui vc passa o nome do mapeamento configurado na classe TabelaA
);

A sacada é a anotação @MappedSuperclass, que o único objetivo dela é fazer a classe TabelaA ser reconhecida como um artefato gerenciado pelo JPA.

Se for um projeto spring, recomendo usar JDBCTemplate com RowMapper.

2 curtidas

Obrigado @Lucas_Camara, era exatamente isso que eu precisava!