@Query com Join Fetch não funciona no Spring Data JPA em conjunto com Pageable
23/11/2018 15:43
0
Recentemente tive problema com uma consulta do Spring Data JPA.
Não conseguia saber o motivo, mas ao utilizar a instrução join fetch estava recebendo a seguinte exceção:
"query specified join fetching, but the owner of the fetched association was not present in the select list"
Isso era no mínimo frustrante, porque eu tinha escrito uma consulta JPQL / HQL padrão, e que tinha certeza que funcionava.
Meu repositório Spring Data JPA estava assim:
@Repository
public interface CidadeRepository extends JpaRepository{
@Query(value="SELECT entity FROM Cidade entity INNER JOIN FETCH entity.estado est  WHERE est.sigla = lower(trim(?1))  OR  lower(entity.descricao) like %?1%")
public Page findAllByDescricao(String filter, Pageable pageable);
}


A mesma coisa aconteceu ao utilizar o novo recurso do JPA 2.1, os "entity graphs".

@Repository
public interface CidadeRepository extends JpaRepository<Cidade, Long>{
  @EntityGraph(value = "graph.uf.cidades", type = EntityGraph.EntityGraphType.FETCH)
@Query(value="SELECT entity FROM Cidade entity INNER JOIN FETCH entity.estado est  WHERE est.sigla = lower(trim(?1))  OR  lower(entity.descricao) like %?1%")
public Page<Cidade> findAllByDescricao(String filter, Pageable pageable);
}
Acredito eu que problema acabou sendo causado pela funcionalidade de paginação fornecida pelo Spring Data JPA, estendendo a interface PagingAndSortingRepository
(estendida pela interface JpaRepository). Sempre que alguém usa essa funcionalidade de paginação, o Spring Data JPA executa duas consultas, sendo
uma para obter a quantidade de linhas correspondentes à consulta, e outra que realmente faz a busca dos dados no banco.
O problema tem a ver como o Spring Data JPA gera a consulta de contagem para obter a quantidade de linhas.
Como a consulta contem a palavra chave FETCH para "forçar um join" da entidade cidade com a entidade estado, cujo respectivos relacionamentos são @ManyToOne e @OneToMany,
no momento de validar a query de contagem, que neste caso seria algo semelhante a isto: select count(cidade0_.id) as col_0_0_ from cidade cidade0_ inner join fetch uf uf1_ on cidade0_.estado_id=uf1_.id where uf1_.sigla=lower(trim(?)) or lower(cidade0_.descricao) like ?
ocorre a excessão lançada pelo Hibernate.
Não examinei profundamente os detalhes desse problema para validar esta teoria, mas acredito que o Spring Data JPA leva a consulta fornecida pela anotação
@Query e tenta construir uma consulta de contagem com base nela.
Como resultado, ele mantem a instrução FETCH, mas o provedor JPA (Hibernate no meu caso) lança uma exceção porque usar a palavra-chave FETCH não tem sentido algum para uma consulta de contagem de registros.
Buscar dados torna-se inútil visto que a consulta esta retornando apenas a quantidade de linhas correspondente, e o Hibernate não sabe como mapear estes dados do resultado para os objetos.
Graças a sacada genial do time do Spring, o Spring Data JPA fornece uma maneira fácil e menos verboso de substituir a consulta de contagem gerada automaticamente,
usando o atributo countQuery na anotação @Query. Nessa consulta deixamos de fora as associações FETCH. Veja bem, deixamos de fora as associações FETCH, mas não os JOINS, ficando desta forma:
@Repository
public interface CidadeRepository extends JpaRepository<Cidade, Long>{
@Query(value="SELECT entity FROM Cidade entity INNER JOIN FETCH entity.estado est  WHERE est.sigla = lower(trim(?1))  OR  lower(entity.descricao) like %?1%",
countQuery="SELECT count(entity) FROM Cidade entity INNER JOIN entity.estado est  WHERE est.sigla = lower(trim(?1))  OR  lower(entity.descricao) like %?1%")
public Page<Cidade> findAllByDescricao(String filter, Pageable pageable);
}
Como pode ser visto, as duas consultas são bem semelhantes, com exceção das palavras-chave FETCH que foi removida.
Isso faz com que o provedor JPA execute uma consulta de contagem correta e, agora o método de repositório agora funciona!
Note que no exemplo acima, o @EntityGraph é inteiramente opcional, e que neste caso foi removido.
Bem é isto, quero apenas compartilhar o problema e forma que resolvi, visto que a mensagem informada pelo Hibernate é menos óbvio do que em muitos outros casos.
Espero que isso ajude você a encontrar a causa do seu problema, porque no meu caso foi um tanto chato de encontrar.
Tags: Spring,@Query, Pagealbe, Join, Fetch


Ainda não faz parte da comunidade???

Para se registrar, clique aqui.

Podcast da itexto



Aprenda Groovy e Grails, Spring e mais com a Formação itexto!

Livro de Spring


/dev/All

Os melhores blogs de TI
em um único lugar!

 
Spring Brasil é mantido por itexto Consultoria.
Em caso de problemas contacte Henrique Lobo Weissmann (Kico) por e-mail: kico@itexto.com.br
Todo o conteúdo presente neste site adota o Creative Commons como licença padrão.