Utilizando o Hibernate podemos armazenar e recuperar objetos Java através do Mapeamento Objeto-Relacional (Object-Relational Mapping - ORM).

O Hibernate possui uma linguagem de consulta para retornar objetos da base de dados (com SELECT) chamada HQL que não se limita apenas a consultas, mas também pode ser usada para alterar informações da base de dados (com INSERT, UPDATE, DELETE). HQL não permite que possamos alterar a estrutura da base de dados.

No restante do artigo veremos mais sobre essa poderosa linguagem que o Hibernate nos oferece.

Hibernate Query Language (HQL)

O Hibernate Query Language (HQL) é uma linguagem de consulta orientada a objetos, muito parecida com o SQL, mas ao invés de efetuar operações nas tabelas e colunas da base de dados, o HQL trabalha com objetos persistentes e suas propriedades, ou seja, classes e propriedades das classes. O HQL inspirou a criação da JPQL (Java Persistence Query Language) que faz parte da especificação oficial da JPA. Quem tem curiosidade para ver a gramática do HQL pode baixar o Hibernate e pesquisar no diretório "grammar" toda a gramática do HQL que é definida como uma gramática ANTLR.

A HQL tem sua própria sintaxe e gramática sendo escritas como strings ao contrário da API Criteria do Hibernate que tem uma forma de uma API convencional do Java. As consultas HQL são traduzidas pelo Hibernate em consultas SQL e, além disso, o Hibernate também provê uma API que permite consultas SQL diretamente.

Uma grande vantagem da HQL é que ela é bastante fácil de ser utilizada e muito intuitiva. O uso da HQL é sempre bastante recomendada para evitarmos problemas de portabilidade e principalmente termos como vantagem a geração do SQL por parte do Hibernate que é feito da melhor forma e mais otimizada além também de utilizar as estratégias de cache que o Hibernate cria.

Uma dica muito importante é no caso de encontrarmos problemas de performance, nesse caso o recomendado é tentarmos sempre otimizar primeiramente o HQL e somente depois tentar utilizar um SQL nativo. É importante lembrar que o Hibernate provê informações estatísticas através do JMX MBean que pode ser utilizado para análises de performance do Hibernate. Estas estatísticas também analisam como está a performance do cache.

Sintaxe Básica

O HQL possui as quatro operações básicas utilizadas para manipulação da base de dados, são elas: SELECT, INSERT, UPDATE, DELETE.

UPDATE altera detalhes de objetos existentes na base de dados. Entidades em memória não são alteradas para refletir alterações realizadas com UPDATE. Segue abaixo um exemplo de como utilizar o comando UPDATE:


  UPDATE [VERSIONED]
           [FROM] path [[AS] alias] [, ...]
           SET property = value [, ...]
           [WHERE logicalExpression]

O atributo path é o nome completo da entidade ou das entidades. Atributos “alias” podem ser utilizados para abreviar referencias para entidades específicas ou suas propriedades. O “alias” também pode ser utilizado quando nomes de propriedades usadas nas consultas são ambíguas. O atributo property são os nomes das propriedades das entidades listadas no path após FROM.

DELETE é responsável por remover detalhes de objetos existentes na base de dados. Entidades em memória não são alteradas para refletir alterações realizadas com DELETE. Isto também significa que regras de cascateamento não são realizadas com o HQL. No entanto, se especificarmos que as deleções devem ser feitas em cascata no nível da base de dados (diretamente ou através da anotação @OnDelete no Hibernate), a base de dados removerá todos os filhos. Segue abaixo um exemplo de como utilizar o comando DELETE:

DELETE
           [FROM] path [[AS] alias]
           [WHERE logicalExpression] 

O atributo path é o nome completo de uma entidade. Atributos “alias” podem ser usados para abreviar referencias para entidades específicas ou suas propriedades. O “alias” também pode ser utilizado quando nomes de propriedades usadas são ambíguas.

INSERT não pode ser utilizado diretamente para inserir entidades arbitrariamente, e sim pode ser utilizado apenas para inserir entidades construídas através de informações obtidas por consultas utilizando SELECT. Isto é diferente da abordagem utilizando SQL onde um comando INSERT pode ser usado para inserir arbitrariamente informação em uma tabela, assim como inserir valores selecionados de outras tabelas. Segue abaixo um exemplo de como utilizar o comando INSERT:


  INSERT
           INTO path ( property [, ...])
           select

O atributo path é o nome de uma entidade. O atributo property é o nome das propriedades das entidades listadas no path após o FROM do SELECT incorporado. O atributo select é uma consulta SELECT em HQL.

Por fim, SELECT é utilizado para consultar classes e suas propriedades na base de dados. Apesar da HQL possuir um poder expressivo altamente poderoso, se quisermos uma maior complexidade utilizando joins e like, devemos optar pela API Criteria que é mais apropriada. Segue abaixo um exemplo de como utilizar o comando SELECT:


  [SELECT [DISTINCT] property [, ...]]
           FROM path [[AS] alias] [, ...] [FETCH ALL PROPERTIES]
           WHERE logicalExpression
           GROUP BY property [, ...]
           HAVING logicalExpression
           ORDER BY property [ASC | DESC] [, ...] 

O atributo path é o nome completo da entidade. Atributos “alias” podem ser usados para abreviar referencias para entidades específicas ou suas propriedades. O “alias” também pode ser utilizado quando nomes de propriedades usadas são ambíguas. O atributo property é o nome das propriedades das entidades listadas no path após FROM. Se utilizarmos o FETCH ALL PROPERTIES as semânticas lazy loading serão ignoradas, e dessa forma todas as propriedades dos objetos recuperados serão ativamente carregados (isto não é aplicado recursivamente). Uma das diferenças quando estamos utilizando JPQL ao invés de HQL é que a JPQL requer a cláusula SELECT, ao passo que no HQL podemos omitir o SELECT.

Um alias pode ser usado com um “AS” definido ou não necessariamente. Abaixo seguem dois exemplos que retornam o mesmo resultado:

from Produto as produto 

ou

from Produto produto 

Se tivermos nomes de classes duplicados na aplicação podemos utilizar o pacote com o nome da classe como abaixo:

from com.hibernateexemplo.produtos.Produto

Outra forma de usar o SELECT é retornar apenas algumas propriedades ao invés do objeto inteiro, como por exemplo:

select produto.nome from Produto produto

Nessa consulta temos uma LISTA de objetos String.

Mas se quisermos retornar o nome e o preço do produto, teríamos o HQL abaixo:

select produto.nome, produto.preco from Produto produto

Dessa forma, teríamos um resultado contendo uma LISTA de ARRAYS de objetos, onde cada array representa um conjunto de propriedades.

Retornando apenas algumas propriedades irá reduzir o tráfego na rede para o servidor da base de dados e também alivia bastante a memória da máquina onde a aplicação está sendo executada.

Uma dica importante é sabermos que as palavras-chaves do HQL podem ser maiúsculas ou minúsculas, portanto nesse caso o Hibernate não é case-sensitive. Por outro lado, o nome das classes e propriedades são case-sensitive. Assim, uma consulta “from Produto” é similar a “FROM Produto”, mas uma consulta “from produto” não é o mesmo que “from Produto”.

Logando o SQL gerado pelo HQL

O Hibernate permite que possamos imprimir o SQL por trás das consultas HQL num arquivo de log da aplicação. Esse SQL gerado pelo Hibernate pode ser executado diretamente na base de dados.

Para isso devemos configurar a propriedade hibernate.show_sql para true no arquivo hibernate.properties ou no arquivo hibernate.cfg.xml. Dessa forma o Hibernate irá imprimir o SQL nos logs.

Outra funcionalidade que o Hibernate nos oferece é o de comentar os SQL gerados. Dessa forma podemos comentar diretamente um objeto Query para uma consulta específica utilizando o método setComment() que recebe uma String como argumento. O método utilizado é visto abaixo:


  public Query setComment(String comment) 

Para utilizar essa funcionalidade também devemos configurar a propriedade hibernate.use_sql_comments para true no arquivo de configuração do Hibernate. Segue abaixo um exemplo de um comentário no Hibernate:


  String hql = "from Produto";
  Query query = session.createQuery(hql);
  query.setComment("Meu HQL para Produtos: " + hql);
  List results = query.list();

E a saída seria como abaixo:

Hibernate: /*Meu HQL para Produtos: 
*/ select produto0_.id as id, produto0_.nome 
as nome2_ from Produto produto0_

Com isso, neste artigo vimos o que é HQL e o poder que essa linguagem de consulta orientada a objetos possui. Com HQL podemos ter o máximo de vantagens do SQL e ainda temos o mapeamento objeto-relacional e funcionalidade de cache para a nossa aplicação. Vimos nesse artigo alguns dos principais comandos do HQL e facilidades como logs que ajudam a encontrar problemas no SQL gerado.

Bibliografia

[1]Hibernate - JBoss Community, disponível em www.hibernate.org/

[2]Documentação de Referência Hibernate, disponível em https://docs.jboss.org/hibernate/core/3.6/reference/pt-BR/html/index.html

[3] Introdução ao Hibernate, disponível em https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/queryhql.html

[4] Jeff Linwood and Dave Minter. An introduction to persistence using Hibernate 3.5, Second Edition. Apress.

[5] Steve Perkins. Hibernate Search by Example: Explore the Hibernate Search system and use its extraordinary search features in your own applications. Packt Publishing.