Hibernate – conceitos e consultas
Jobson Ronan {[email protected]}
Objetivos


Aprender sobre os conceitos de persistência
relacionados ao Hibernate
Aprender como recuperar os dados e efetuar
consultas de maneira eficientemente no Hibernate
Ciclo de vida da persistência


Como Hibernate oferece um sistema transparente de
persistência, as classes não sabem de sua capacidade
de persistência
Por outro lado, a aplicação que usa os objetos, os utiliza
em estados diferentes



Transientes, antes de serem gravados em meio persistente
Persistentes, quando estão armazenados
Desligados, quando suas instâncias são manipuladas sem
afetar a base de dados
Diagrama de estados
Objetos transientes

Objetos que acabaram de ser criados (com new) ainda não são
persistentes



Instâncias transientes são não-transacionais



Seu estado é transiente (ainda não foram armazenados no banco e
deixarão de existir assim que perderem sua referência)
Session.delete() sobre um objeto persistente torna-o transiente
Rollback não recupera seu estado anterior
Objetos referenciados por instâncias transientes são (por default)
também transientes
Para mudar para o estado persistente é preciso


Passar o objeto como argumento de um Session.save(), ou
Criar a referência a partir de uma instância persistente
Objetos persistentes

Uma instância persistente é uma instância com uma
identidade no banco de dados


Podem ser





Tem uma chave primária como identificador
Objeto criado com new e armazenado com Session.save()
Objeto criado a partir da referência de uma instância
persistente
Objeto obtido a partir de um query no banco
Estão sempre associados com uma Session
São transacionais


Seu estado é sincronizado com o banco ao fim da transação
Automatic dirty checking (transparente ao usuário) é usado
como estratégia de atualização eficiente de dados
Objetos desligados (detached)

Quando a sessão é fechada (Session.close())
todas as instâncias ainda mantém seu estado, mas não
estão mais sincronizadas com o banco




Podem ficar obsoletas se houver mudança no banco
Podem mudar de estado, que não será refletido no banco
Mas, a qualquer momento, a sessão pode ser reativada,
tornando o objeto persistente e sincronizando seu estado
Objetos desligados são úteis para transportar o estado de
objetos persistentes para outras camadas

Camada de apresentação, em substituição aos DTOs (Data
Transfer Objects, também chamados de Value Objects)
Como tornar um objeto persistente

1) Crie o objeto e inicialize suas propriedades
User user = new User();
user.getName().setFirstname("John");
user.getName().setLastname("Doe");

2) Abra uma sessão
Session session = factory.openSession();

3) Inicie uma transação
Transaction tx = session.beginTransaction();

4) Grave o objeto
session.save(user);

5) Cometa a transação
tx.commit();

6) Feche a sessão
session.close();
Como atualizar estado de instâncias desligadas

Quando a sessão for fechada, user torna-se uma
instância desligada


Qualquer alteração no seu estado não afeta o banco
Para religá-lo pode-se usar update()
user.setPassword("secret"); // objeto desligado
Session sessionTwo = sessions.openSession();
Transaction tx = sessionTwo.beginTransaction();
sessionTwo.update(user);
user.setUsername("jonny"); // objeto persistente
tx.commit();
sessionTwo.close();
Como recuperar um objeto persistente

A forma mais simples de recuperar um objeto é pelo
identificador, usando o comando get():
Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
int userID = 1234;
User user = (User) session.get(User.class, new Long(userID));
tx.commit();
session.close();


Uma vez que a sessão foi fechada, o objeto é uma
instância desligada, e pode ser repassada para outras
camadas (apresentação, por exemplo)
Se o objeto não existir, a chamada get() retorna null
Como atualizar um objeto persistente

Qualquer objeto retornado por get() é um objeto persistente




Quaisquer modificações no seu estado serão sincronizadas com
o banco de dados
Hibernate automaticamente verifica e grava as mudanças
ocorridas dentro de uma sessão (automatic dirty checking)
As mudanças tornam-se permanentes ao cometer a transação
O objeto torna-se desligado quando a sessão fecha
Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
int userID = 1234;
User user = (User) session.get(User.class, new Long(userID));
user.setPassword("secret");
tx.commit();
session.close();
Como tornar transiente um objeto persistente

Um objeto persistente pode tornar-se transiente se for
removido do banco


Para isto é preciso usar o gerente de persistência e chamar o
método delete()
Quando a sessão terminar o objeto será considerado um mero
objeto Java transiente (cujo estado será perdido assim que o
coletor de lixo atuar)
Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
int userID = 1234;
User user = (User) session.get(User.class, new Long(userID));
session.delete(user);
tx.commit();
session.close();
Como tornar transiente um objeto desligado


Para tornar um objeto desligado transiente, não é preciso
ligá-lo ou fazer update()
Basta chamar delete() sobre sua instância
Session session = sessions.openSession();
Transaction tx = session.beginTransaction();
session.delete(user);
tx.commit();
session.close();
API Session: ciclo de vida (Resumida)

beginTransaction()


close()


Desconecta a sessão da conexão JDBC atual sem fechá-la.
reconnect()


Fecha e desconecta a sessão
disconnect()


Demarca o início de uma transação. Retorna um objeto Transaction que
deve chamar commit() ou rollback() no final da transação.
Obtém uma nova conexão JDBC para a sessão ou tenta conectar a uma
conexão JDBC, se passada como argumento
flush()

Sincroniza a camada de objetos com a camada de dados. Este método é
chamado automaticamente quando a transação é cometida.
Session: gerência de persistência (Resumida)

save(Object)


saveOrUpdate(Object)


Carrega e retorna a instância de objeto identificado pela classe e identificador
refresh(Object)


Remove os dados de um objeto do banco, tornando-o transiente
load(classe, identificador)


Atualiza o objeto passado como argumento
delete(Object instancia)


Insere (tornando persistente) ou atualiza o objeto passado como argumento
update(Object)


Torna persistente o objeto passado como argumento
Recarrega do banco os dados da instância passada como argumento
evict(Object)

Remove o objeto passado como argumento do cache do sistema
Recuperação de dados

Formas de recuperar objetos





Pelo identificador
Navegando na árvore de objetos
[ ex: usuario.getEndereco().getCidade() ]
Usando HQL
Usando a API Criteria
Usando SQL nativo
Recuperação por identificador

Há dois métodos de Session para recuperar objetos pelo
identificador


Object get(Object id)


Ambos utilizam o cache, e evitam acessar o banco se não for
necessário
Devolve o objeto se existir e null se o objeto não for
encontrado no banco ou no cache
Object load(Object id)


Devolve o objeto ou um proxy para o objeto se existirem, ou
causa exceção se nenhum for encontrado no banco ou no
cache
O proxy pode apontar para objeto que ainda ou não mais
existe
HQL

Hibernate Query Language é um dialeto orientado a
objetos do SQL




Assemelha-se a ODMG OQL e EJB-QL mas é adaptado para
uso em bancos de dados SQL
Não é uma linguagem de manipulação de dados (como SQL);
não serve para inserir, remover, atualizar
É usada apenas para recuperação de objetos
Exemplo
Query q =
session.createQuery("from User u where u.firstname = :fname");
q.setString("fname", "Max");
List result = q.list();
HQL

HQL suporta vários recursos úteis e avançados; entre
eles








Pesquisas com navegação do grafo de objetos
Recuperação seletiva de propriedades dos objetos
selecionados (sem ter que carregar objeto inteiro)
Ordenação de resultados
Paginação de resultados
Agregação com group by, having, e funções sobre agregados
como sum, count, min e max
Outer joins ao recuperar múltiplos objetos por registro
Chamadas a funções SQL definidas pelo usuário
Subqueries
Consulta usando Criteria (QBC)


QBC = Query By Criteria
Queries podem ser expressos usando uma API em vez de usar
HQL





Evita uso de strings
Validação é feita em tempo de compilação
Enfoque mais orientado a objetos
Mais difícil de ler (é Java e não HQL)
Exemplo


Em HQL: from User u where u.firstname = :fname
Em QBC:
Criteria criteria = session.createCriteria(User.class);
criteria.add( Expression.like("firstname", "Max") );
List result = criteria.list();
Como achar os dados?


Talvez o problema mais difícil de solucionar em ORM:
acesso eficiente a dados relacionais
Aplicação prefere tratar os dados como um grafo de
objetos interligados por associações



É mais fácil é fazer vários pequenos queries (performance
baixa)
Alternativa: escrever queries para cada associação (muito
complexo e pouco flexível)
Hibernate permite especificar uma estratégia de
recuperação default (no mapeamento) que pode ser
sobreposta em tempo de execução
Estratégias de recuperação


Fetching strategies
Estratégias para qualquer associação (nos metadados e
em tempo de execução)



Lazy fetching – objeto associado só é recuperado quando
chamado
Eager fetching – objeto associado é recuperado através de
SQL outer join
Batch fetching – acessa uma coleção de objetos prédeterminada
Lazy fetching

Permite que apenas parte do grafo de objetos seja
carregado inicialmente




O restante do grafo pode ser carregado à medida em que for
necessário
Vai requer mais chamadas ao banco posteriormente, mas pode
ser que elas não sejam necessárias
Pode ser otimizada com Batch Fetching
Deve ser a estratégia inicial para todas as associações
no arquivo de mapeamento

A estratégia pode depois ser sobreposta em tempo de
execução por estratégias mais ávidas
LazyInitializationException

Erro comum, causado por inicialização lazy


A forma mais simples de evitá-lo, é realizar tudo dentro da sessão (o
commit() sincroniza os dados e evita a inconsistência)
Sabendo-se do estado do objeto, os dados podem ser recuperados em
outra sessão (quando voltar a ser persistente)
s = sessions.openSession();
User u = (User) s.createQuery("from User u where
u.name=?“).setString(userName).list().get(0);
Map permissions = u.getPermissions();
s.connection().commit();
s.close();
Integer accessLevel =
(Integer) permissions.get("accounts"); // Error!
Recuperação em lote (Batch fetching)

Na verdade não é uma outra estratégia




É uma solução rápida para otimizar lazy fetching
É um lazy menos lazy: usuário define quantos níveis
de associações devem ser carregadas
Hibernate procura as outras instâncias associadas
à coleção e tenta carregá-las ao mesmo tempo
Pode ser melhor que lazy simples

Menos chamadas ao banco
Eager fetching

É uma solução que ajuda a reduzir a carga sobre o banco
de dados de forma mais inteligente que Batch Fetching



Permite que se especifique explicitamente quais objetos
associados devem ser carregados juntos com o objeto
que os referencia
Os objetos associados podem então ser retornados em
única requisição


Pode ser um a boa estratégia default
Usa SQL outer join
Otimização de performance em Hibernate
freqüentemente usa esse tipo de estratégia em tempo de
execução para um query em particular
Qual estratégia usar?


A seleção de uma estratégia de recuperação de dados
default é feita através de atributos no XML do arquivo de
mapeamento.
Mapeamentos de coleção diferem dependendo do tipo de
associações.

Coleções e associações de muitos para muitos comportam-se
diferentemente de associações singulares “-to-one”.
outer-join em associações “-to-one”

O atributo outer-join sobrepõe lazy fetching



Opções




Usado do lado oposto da associação
O outro lado escolhe lazy fetching (ou immediate fetching por omissão)
outer-join=“auto” (default).
O comportamento é lazy, se o outro lado (singular) da associação
especificar, ou eager, se não especificado.
outer-join=“true”
Comportamento é sempre eager para esta associação (logo diferentes
associações para mesmo objeto podem ter estratégias diferentes)
outer-join=“false”
Se o objeto tiver sido mapeado como lazy, ocorre lazy fetching, caso
contrário, immediate fetching (SQL select)
Exemplo:
<many-to-one name="item" class="Item" outer-join="true">
Em coleções

Lazy

Use atributo lazy=“true” (não depende de proxy do outro lado)
<set name="bids" lazy="true">

Batched Lazy

Acrescente o atributo batch-size=“tamanho” (refere-se ao
número de coleções
<set name="bids" lazy="true" batch-size="9">

Eager


Use atributo outer-join=“true”
Evite usar como opção default
Consultas HQL

O query mais simples possível
Query q1 = session.createQuery("from User");


Os resultados de um query podem ser lidos em “páginas”
O query abaixo lê 10 páginas, a partir da primeira página
Query query =
session.createQuery("from User u order by u.name asc");
query.setFirstResult(0);
query.setMaxResults(10);
List results = query.list();

Listagem de resultados
List result =
session.createQuery("from User").list();
Passagem de parâmetros

Evite montar queries via concatenação de strings




Risco de segurança: basta esquecer de fechar a aspa
Longos queries ficam ilegíveis
O ideal é passar parâmetros que serão ligados ao query
Parâmetros podem ser passados duas formas


Por nome de variável
Por ordem (como PreparedStatement em java.sql)
Passagem de parâmetros

Parâmetros passados por nome

No query, o nome deve ser precedido de um “:”
String queryString = "from Item item where “
+ “item.description like :searchString";

Parâmetros por posição
String queryString = "from Item item "
+ "where item.description like ? "
+ "and item.date > ?";
List result = session.createQuery(queryString)
.setString(0, searchString)
.setDate(1, minDate)
.list();
Queries chamados pelo nome

Queries não precisam aparecer no código



Na verdade, muitas vezes é melhor que não apareçam
Podem ficar nos metadados e serem chamados pelo nome
Para chamar um query pelo nome, use getNamedQuery()
List result = session.getNamedQuery("findItemsByDescription")
.setString("description", description).list();

Mas antes, é preciso que ele seja declarado em algum
arquivo de mapeamento (ex: Item.hbm.xml):
<query name="findItemsByDescription"><![CDATA[
from Item item where item.description like :description
]]></query>
Aliases

Aliases (apelidos) são usados para que se possa ter uma
referência para acessar propriedades das instâncias
recuperadas
from Livro

é suficiente para recuperar todos os livros
Um alias é declarado da seguinte forma
from Livro as livro

Mas o as é opcional
from Livro livro

Exemplo: para testar as propriedades em um where
from Livro livro
where livro.codigo = ‘005.133’
Queries polimórficos


Considere a hierarquia ao lado
O query abaixo é polimórfico
BillingDetails
from BillingDetails


pois os resultados são classes concretas CreditCard BankAccount
BankAccount e CreditCard
Queries polimórficos podem ser feitos em qualquer classe, não
apenas em classes mapeadas
O query abaixo retorna todas as instâncias mapeadas
from java.lang.Object

Criteria também suporta polimorfismo
List res =
session.createCriteria(java.lang.Object.class)
.list();
Mais HQL: Restrições (where)


Como em SQL, HQL define restrições de um query através da
cláusula where, que suporta várias expressões.
Por exemplo, a expressão de equivalência:
from User u where u.email = '[email protected]‘

A expressão where pode ter resultado true, false ou null



HQL suporta os mesmos operadores que SQL
Queries Criteria: expressões na classe Expression


lógica ternária
Não há suporte para expressões aritméticas via API (só via HQL)
Operadores do HQL:



Comparação: =, <>, <, >, >=, <=, between, not between, in, not in
Nulidade: is null, is not null
Aritméticos: +, -, /, *, %, parênteses
Mais HQL: Restrições (where)

Exemplos
from
from
from
from
from
Bid bid where bid.amount between 1 and 10
Bid bid where bid.amount > 100
User u where u.email in ( "[email protected]", "[email protected]" )
User u where u.email is null
Bid bid where ( bid.amount / 0.71 ) - 100.0 > 0.0
Mais HQL: strings

O operador like funciona da mesma forma que SQL



O símbolo _ representa um caractere
O símbolo % representa vários caracteres
Exemplos


HQL: from User u where u.firstname like "G%"
Criteria: session.createCriteria(User.class)
.add(Expression.like("firstname","G%"))

Pode-se usar funções SQL se banco suportar
from User u where lower(u.email) = '[email protected]'

Não há um operador padrão de concatenação de strings


É preciso usar recursos do banco nativo.
Exemplo:
where (u.fname || ' ' || u.lname) like 'G% K%'
Mais HQL

Operadores lógicos: and, or, parênteses, etc. servem
para argupar expressões
from User user
where ( user.firstname like "G%" and
user.lastname like "K%" ) or
user.email in ( "[email protected]", "[email protected]" )

Ordenação: Semelhante a SQL: order by


asc – ascendente (default)
desc – descendente
from User u order by u.lname asc, u.fname asc

...Pensou que acabou?
Joins


Joins (junções) são usados para combinar dados de duas ou mais
tabelas relacionadas
Há quatro tipos de joins possíveis





Além disso, há dois estilos em SQL



Inner join
Left outer join
Right outer join
Full join
ANSI: junção de tabelas através de propriedade comum com condição de
join na cláusula on
Theta: produto cartesiano de tabelas com condição de join na cláusula
where
Joins em HQL são bem mais simples que em SQL
SQL: ANSI inner join e left outer join
 Inner join (ou
simplesmente “join”) apenas Itens com Bids
 Left outer join
(ou “left join”) - Inclui
também Itens que
não têm Bids


Right outer join (“right join”) - permite valores nulos na tabela esquerda (inclui
Bids sem Itens mas não itens sem Bids) – não faz sentido neste caso (lance sem
item)
Full join - permite valores nulos nas duas tabelas (inclui Bids que não têm Itens
e itens que não têm Bids) – também não faz sentido neste caso
Joins em HQL

Em Hibernate, geralmente condições de join não são
especificadas explicitamente



Elas podem ser deduzidas das associações entre objetos
automaticamente
Queries ficam mais simples e legíveis
Quatro maneiras de expressar joins em HQL




Fetch join (outer, agressivo) na cláusula from
Join comum (inner, lazy) na cláusula from
Join theta-style (produto cartesiano) na cláusula where
Join por associação explícita
Fetch join


Recupera objeto inteiro (com associações inicializadas.)
Exemplo HQL:
from Item item
left join fetch item.bids
where item.description like '%gc%‘
Retorna lista de
objetos com coleções
já inicializadas
Join simples (inner) com alias


Freqüentemente é necessário aplicar restrições a tabelas
combinadas com join.
Isto pode ser feito atribuíndo um alias ao join:
from Item item
join item.bids bid
where item.description like '%gc%‘
and bid.amount > 100

O query acima é um inner join



Em vez de uma lista de Item, retorna uma lista de Object[]
Retorna uma lista de arrays {item, bid} (Se fosse um fetch join, retornaria
uma lista de Item com coleção de bids inicializada)
Um Item pode aparecer múltiplas vezes (uma vez para cada Bid associado)
inner join x outer (fetch) join

Como obter os dados usando um inner join
Query q = session.createQuery("from Item item join item.bids bid");
Iterator pairs = q.list().iterator();
Retorna pares
while ( pairs.hasNext() ) {
de referências
Object[] pair = (Object[]) pairs.next();
Apenas Items
Item item = (Item) pair[0];
que têm Bid
Bid bid = (Bid) pair[1];
}

E usando um left outer join com fetch
Query q = session.createQuery("from Item item left join fetch item.bids");
Iterator items = q.list().iterator();
Retorna Items
while ( items.hasNext() ) {
Item item = (Item) items.next();
Todos os Items
Bid bid = (Bid) item.getBid();
inclusive os que
}
não têm Bid
Cláusula select

Pode-se restringir os objetos retornados com uma cláusula select



Select é opcional em HQL (mas não em SQL)
A ausência do select equivale ao SQL select *
Para que o inner join do exemplo anterior devolva apenas os itens
(e não uma lista de Object[] com par item-bid) pode-se usar select:
select item
from Item item
join item.bids bid

Exemplo
Query q = session.createQuery("select i from Item i join i.bids b");
Iterator items = q.list().iterator();
while ( items.hasNext() ) {
Item item = (Item) items.next();
}
Agora temos uma lista de Items contendo
apenas os Items que têm Bids (inner join)
Joins implícitos


Ocorrem em associações *-para-um (caminho convergente) e
nunca em caminhos *-para-muitos
O HQL a seguir causa três joins em SQL
from Bid bid
where bid.item.category.name like 'Laptop%'
and bid.item.successfulBid.amount > 100

E pode ser expresso explicitamente, usando
from Bid bid
join bid.item item
where item.category.name like 'Laptop%‘
and item.successfulBid.amount > 100

Ou ainda
from Bid as bid
join bid.item as item
join item.category as cat
join item.successfulBid as winningBid
where cat.name like 'Laptop%‘
and winningBid.amount > 100
Joins implícitos
sendo revelados
Produtos cartesianos (theta style joins)


Permite recuperar todas as combinações possíveis de instâncias
de duas ou mais classes
Útil em classes não associadas



Condição de join deve estar na cláusula where
Apenas inner-join pode ser usado neste caso
Produto cartesiano
Exemplo
Query query = session.createQuery("from User user, LogRecord log " +
"where user.username = log.username")
Iterator i = query.list().iterator();
while ( i.hasNext() ) {
Object[] pair = (Object[]) i.next();
Condição de join
User user = (User) pair[0];
LogRecord log = (LogRecord) pair[1];
}

Pode-se usar uma cláusula select para filtrar os resultados
Comparação de identificadores

Queries que comparam chaves-primárias são realizados
implicitamente em HQL


Os dois queries ....



É o mesmo que comparar instâncias
from Item i, User u
where i.seller = u and u.username = 'steve'
from Item i, Bid b
where i.seller = b.bidder
São respectivamente equivalentes aos queries:


from Item i, User u
where i.seller.id = u.id and u.username = 'steve'
from Item i, Bid b
where i.seller.id = b.bidder.id
Esta última sintaxe permite que se passe
parâmetros (tipo, o id) para comparação.
Queries de relatórios

Relatórios geralmente focam nos dados



Instâncias não são importantes, mas valores selecionados,
agrupados e organizados de certas instâncias
Dependem de recursos de seleção e agrupamento
Usa-se cláusulas select e group by / having.
Projeção: cláusula select


Quais os dados desejados no resultado?
O query abaixo seleciona três propriedades de dois objetos


Os dados serão retornados em um vetor de Object[] de tamanho 3.
Os resultados não são entidades (são valores) e não são transacionais
(serão usados para leitura, apenas)
Query query =
session.createQuery("select item.id, item.description, bid.amount "
+ "from Item item join item.bids bid where bid.amount > 100");
Iterator i = query.list().iterator();
while ( i.hasNext() ) {
Object[] row = (Object[]) i.next();
Long id = (Long) row[0];
String description = (String) row[1];
BigDecimal amount = (BigDecimal) row[2];
ItemRow itemRow = new ItemRow(id, description, amount)“;
}
Instanciamento dinâmico

Hibernate permite o instanciamento dinâmico de objetos que são
povoados pelos resultados do query


Evita o uso de vetores de objetos
É necessário que exista uma classe e construtor previamente construída
class ItemRow {
public ItemRow(Long id, String description, BigDecimal amount) {...}
...
}
Query query =
session.createQuery(
"select new ItemRow(item.id, item.description, bid.amount)"
+ "from Item item join item.bids bid where bid.amount > 100");
Iterator i = query.list().iterator();
while ( i.hasNext() ) {
ItemRow row = (ItemRow) i.next();
// ... fazer alguma coisa
}
Distinct


Com o uso da cláusula select, os resultados de um query não mais
tem garantia de serem distintos
A seguinte query pode retornar Items repetidos
select item.description
from Item item

Para evitar duplicação, use a cláusula distinct
select distinct item.description
from Item item
Funções agregadas

HQL suporta as seguintes funções na cláusula select


count() – retorna Integer
min(), max(), sum() e avg() – retornam BigDecimal
Integer count = (Integer)
session.createQuery("select count(*) from Item").uniqueResult();
BigDecimal sum = (BigDecimal)
session.createQuery("select sum(item.successfulBid.amount) "
+"from Item item").uniqueResult();
BigDecimal[] minmax = (BigDecimal[])
session.createQuery("select min(bid.amount), max(bid.amount) “
+"from Bid bid where bid.item.id = 1")
.uniqueResult();
BigDecimal min = minmax[0];
BigDecimal max = minmax[1];
Agrupamento

Se houver funções agregadas no select, não pode haver seleção
de outros elementos, a menos que haja uma cláusula group by

Neste caso, os elementos deverão estar também no group by
select bid.item.id, count(bid), avg(bid.amount)
from Bid bid
where bid.item.successfulBid is null
group by bid.item.id
select bidItem.id, count(bid), avg(bid.amount)
from Bid bid
join bid.item bidItem
where bidItem.successfulBid is null
group by bidItem.id
select item.id, count(bid), avg(bid.amount)
from Item item
fetch join item.bids bid
where item.successfulBid is null
group by item.id
Um inner join
implícito
Um inner join
explícito
Um fetch (outer) join; o
agrupamento teve que
ser feito usando item.id
Restrição de grupos com having

Pode-se aplicar restrições no agrupamento com a cláusula having
select user.lastname, count(user)
from User user
group by user.lastname
having user.lastname like 'A%‘


Grupos requerem o uso de pelo menos dois aliases no select (a
função e a propriedade de agrupamento)
Sempre que a cláusula select tem múltiplos aliases, o Hibernate
devolve o resultado como (tuplas)vetores de Object



Chato de manipular; inseguro quanto ao tipo
Ideal é usar select new e instanciar objetos dinamicamente
Agrupamento com instanciamento dinâmico
select new UsuarioReport(user.lastname, count(user))
from User user
group by user.lastname
having user.lastname like 'A%‘

...agora acabo! 
Referências


Hibernate Reference Documentation
Hibernate in Action
Download

Hibernate – consultas e conceitos de persistencia