Acredito que dentre aqueles que trabalham com o NHibernate (e até mesmo outros ORMs), vários já tenham se perguntado em algum momento o porquê de se utilizar – no caso aqui – a HQL para realizar uma operação no banco ao invés de trabalhar com Criteria, QueryOver ou os métodos convencionais de inserção e remoção: _“Se fosse pra escrever o comando eu usava ADO direto […]”, já devem ter pensado.

E acredito até mesmo que já tenham ficado por alguns minutos parados em frente ao compilador refletindo se valia a pena usa-lo no código. Se não iria deformar o sistema como uma enorme cicatriz no meio do peito – e claro, exposto no GitHub para que os Trolls de plantão tivessem motivo de felicidade.

Quero deixar claro neste momento do texto que não critico ou condeno a utilização de um ou de outro método, apenas transcrevo o que já percebi por ai a fora. Afinal acredito realmente que não há maneira certa ou errada de atingir um objetivo, apenas maneira melhores ou piores conforme o contexto.

Um simples problema para ilustar

Imaginemos um cenário – hipotético e não necessariamente o melhor, mas que nos dará a devida ideia do que quero demonstrar – onde temos uma lista com 5000 produtos, e que estes tenham de ser excluídos do sistema. Para aumentar um pouco mais a complexidade, estes produtos devem ser excluídos da tabela de produtos, porém devem existir registros em uma tabela “auditoria” com informações suficientes para que eu saiba que este produto já existiu e consiga algumas informações relevantes sobre ele, por exemplo, qual estagiário o excluiu, e se possível voltá-lo para o lugar de onde ele não devia ter saído.

(Não vamos entrar em méritos de exclusão lógica ou qualquer outra coisa, como disse, o que vale é ilustrar a ideia).

 

Usando um dos caminhos convencionais possíveis, poderíamos fazer da seguinte forma:

            using (var transaction = Session.BeginTransaction())
            {
                produtos.ForEach(p =>
                                     {
                                         Session.Save(DomainObjects.Factory.CreateProductAuditing(p, AuditingType.Remove));
                                         Session.Delete(p);
                                         Session.Flush();
                                     });
                transaction.Commit();
            }

O problema de trabalharmos desta forma, é que a operação se torna muito cara (lembrando que em alguma momento você teve de carregar a lista de produtos para a memória só para poder excluir depois).

Para estas situações, que envolvem exclusão/atualização/inserção de dados em massa, a HQL fornece uma saída com melhor desempenho, usando um comando como este:

            using (var transaction = Session.BeginTransaction())
            {

                const string hql = @" insert into ProductAuditing "
                                   + " (ProductId, Name, Description) "
                                   + " Select Id, Name, Description "
                                   + " from Product where Customer = :id ";

                Session.CreateQuery(hql)
                    .SetInt32("id", Customer.Id)
                    .ExecuteUpdate();
                transaction.Commit();
            }

Nos testes que fiz, apenas a operação de inserção no cenário acima gastou pouco mais de 6 minutos para ser concluída, enquanto a mesma massa utilizando HQL gastou pouco mais de 3 segundos.

Então o uso do HQL para as operações que envolvam massas de dados grandes é muito bem vindo, mesmo que em todo o restante do sistema tenha-se usado apenas Criteria por exemplo. E convenhamos que nem ficou tão feio assim, ainda mais ganhando tanto em desempenho… seu cliente vai dar pulinhos de alegria =]

Espero ter podido ajudar alguém (ou pelo menos aliviar sua conciência).

Enjoy!