Como usa projections com o hibernate?

Ola,
O meu problema é o seguinte:

Usando Spring-date, se seu criar uma query nativa, direto no repository. Eu nao preciso fazer nada, crio a projection, e uso ela no retorno do metodo e funciona normalmente. Da forma como esta abaixo.
@Repository
public interface Produtos extends JpaRepository<Produto, Long>, ProdutosQueries {

@Query(value = "select p.idproduto,p.ean,p.descricao, pp.custo, pf.referencia from produto p"
		+ " inner join produto_preco pp on p.idproduto=pp.idproduto and pp.id_loja=:idLoja"
		+ " left join itensfornecedor pf on p.idproduto=pf.idproduto and" 
        + " pf.idfornecedor=:idFornecedor "
		+ " where p.ean in (:codigos)" , nativeQuery = true)
public List<TrocaFornecedorItem> listarProdutosPorEan(@Param("codigos") Set<Long> codigos, @Param("idLoja") Long idLoja,
		@Param("idFornecedor") Long idFornecedor);

agora se eu retirar esse metodo da Interface repository e coloca-lo numa classe a query nao cria a projection para retorno de maneira alguma, fica dando erro de que nao tem conversor para retorno dos dados. E é dessa forma que nao funciona, vou apenas suprimir o select pq eh um pouco grande

public class ProdutosImpl implements ProdutosQueries {

@PersistenceContext
private EntityManager manager;

@SuppressWarnings("unchecked")
@Override
public List<PedidoCompraItem> listarItensPedidoCompra(ProdutoFilter produtoFilter) {
	String sql = "select * from produto";
	return manager.createNativeQuery(sql)
			.setParameter("idFornecedor", produtoFilter.getIdFornecedor())
			.setParameter("idLoja", produtoFilter.getIdLoja())
			.setParameter("idProduto", produtoFilter.getIdProduto())
			.setParameter("idGrupo", produtoFilter.getIdSecao())
			.setParameter("idSubGrupo", produtoFilter.getIdGrupo())
			.setParameter("idSubGrupo1", produtoFilter.getIdSubgrupo())
			.setParameter("descricao", produtoFilter.getDescricao())
			.setParameter("participaCotacao", produtoFilter.getParticipaCotacao())
			.setParameter("idFamilia", produtoFilter.getIdFamilia())
			.setParameter("idSituacao", produtoFilter.getIdSituacao())
			.getResultList();
}

por isso estou abrido o topico tem outra forma de avisar ao EM qual a projection ele vai usar?

olha sei que não responde bem sua pergunta ,mas caso não conseguiu use a jdbc e monte a query conforme está na classe e passa os parâmetros e pronto não irá dar nenhum erro.Espero ter ajudado

Agora se optar por usar o hibernate mesmo dê uma olha neste post da Dev Media:

Com JdbcTemplate fica simples e sem mistérios:

https://www.mkyong.com/spring/spring-jdbctemplate-querying-examples/

é também pode ser uma alternativa,mesmo esqueci pois geralmente só uso a jdbc.
*Jdbc sem spring framework incorporado

Ao invés da projection, você pode fazer um select new construtor:

"SELECT NEW MinhaClasse(m.id, m.nome) from MinhaClasse m"

Tentou isso:

String sql = "select * from produto";
	return manager.createNativeQuery(sql, PedidoCompraItem.class)
            .setParameter("param1", valor1);

?

essa abordagem so funciona se a classe for anotada com @Entity. Eu estou usando classes DTO para fazer as leituras do banco, pq usar as entitys o hibernate faz selects demais e desnecessarios.

Ja usei o JDBC Template, tenho uns projetos com ele. De fato eu acho que vai ser uma alternativa a se pensar, porque o jpa eh muito bom, mas para evitar um monte de select, vou ver se vale mais a pena usa-lo pois to escrevendo para consultas sempre nativequeries. O JPA ta servindo so para salvar, pq ate os updates to fazendo com query nativa.

eu tenho essa abordagem de montar a consulta com criteria, e funciona bem. A questao é que ela foi marcada como deprecated, e a nova forma de fazer eu nao vi ainda

@SuppressWarnings("unchecked")
@Override
@Transactional(readOnly = true)
public Page<Produto> filtrarPrecoPendente(ProdutoFilter produtoFilter, Pageable pageable) {
	Criteria criteria = manager.unwrap(Session.class).createCriteria(Produto.class);

	paginacaoUtil.preparar(criteria, pageable);
	adicionarFiltro(produtoFilter, criteria);

	return new PageImpl<>(criteria.list(), pageable, total(produtoFilter));
}

private void adicionarFiltro(ProdutoFilter filtro, Criteria criteria) {
	if (filtro != null) {
		if (!StringUtils.isEmpty(filtro.getDescricao())) {
			criteria.add(Restrictions.ilike("descricao", filtro.getDescricao(), MatchMode.ANYWHERE));
		}

		if (filtro.getIdSecao() != 0) {
			criteria.add(Restrictions.eq("idgrupo", filtro.getIdSecao()));
		}

		if (filtro.getIdGrupo() != null) {
			criteria.add(Restrictions.eq("idsubgrupo", filtro.getIdGrupo()));
		}

		if (filtro.getIdSubgrupo() != null) {
			criteria.add(Restrictions.eq("idsubgrupo1", filtro.getIdSubgrupo()));
		}

		if (filtro.getEntradaDe() != null) {
			criteria.add(Restrictions.ge("dt_entrada", filtro.getEntradaDe()));
		}

		if (filtro.getEntradaAte() != null) {
			criteria.add(Restrictions.le("dt_entrada", filtro.getEntradaAte()));
		}
	}
}

Além do jdbcTemplate ser mais leve do que qualquer recurso que implemente jpa. Jpa/Hibernate é pra quem nao sabe SQL.

1 curtida

Acredito que implementação deveria ser feita com base na Entity, não nas DTO’s.

A execução de muitos select’s para uma consulta deve ser analisada, na maioria dos casos o uso de um fetch join na entidade relacionada resolve.

1 curtida

Concordo.

Discordo, até porquê, quem não sabe criar uma query com SQL nativo, não vai conseguir criar uma query com nenhum tipo de recurso.

1 curtida

nao concordo tanto. Por exemplo, eu quero fazer uma pesquisa. Por exemplo uma entidade produto. Na minha pesquisa eu quero retornar apenas o id, o codigo de barras, a descricao, o preco, o estoque, a loja que o produto pertence e uma quantidade de vendas, isso envolve 5 entidades. Produto, Empresa, ProdutoEmpresa, Venda, ItensDaVenda. Eu vou dispor essas informacoes em uma tabela com a opcao de selecionar o produto que eu quero, baseado no seu estoque e na sua venda. Nao tem porque eu usar Entity, eu uso uma projection ou um objeto dto com esses atributos ai. E mando para a minha tela. Na tela eu devolvo para o servidor, o id do produto, a loja e a quantidade que eu quero comprar para repor meu estoque. ai agora eu uso a Entity, recupero o produto pelo id, monto a entity que eu quero salvar e salvo. Ja pensou se eu fosse montar isso por JPA, ia ser muito mais complexo, alem de ter que carregar todas as outras infinidades de campo da tabela, quando eu so preciso de 9 colunas. Por isso eu uso DTO ou Projections

Não necessariamente, você pode montar via construtor como eu falei, simples até

MinhaClasse(Long id, Long codigoDeBarra, String descricao, Double preco) {
    this.id = id.
    Produto produto = new Produto();
    produto.setCodigoDeBarra(codigoDeBarra);
    produto.setDescricao(descricao);
    produto.setPreco(preco);
    ...
}
"SELECT NEW MinhaClasse(c.id, p.codigoBarra, p.descricao, p.preco) from Minha Classe c ..."

Não acha viável? Você seleciona só os campos que quer

Acho que não me expliquei muito bem, não questionei sobre o uso de DTO’s e Projections para representação das informações que você quer exibir, e sim de como a consulta está sendo montada, com o JPA por exemplo as consultas são feitas a partir da Entity, posso manipular isso com native query, jpql ou até mesmo com criteria, mas sempre partindo das entities, agora a representação deste retorno sim eu manipulo da forma que eu queira apresentar usando classes para projetar estes dados, isso que eu quis dizer.

Eu faco assim tambem, quando vai envolver apenas uma unica entidade. mas quando envolve mais a jpq fica muito complexa e nem sempre funciona bem, quando voce quer fazer um not in, in, left join, subqueries, etc… tem coisas que nao ficam legais com jpql.

entendi.