Estava desenvolvendo uma aplicação utilizando JSF, RichFaces, JBossSeam, Hibernate entre outros. E tive a necessidade de fazer uma subquery. Tentei vários foruns e blogs, mas todos confusos. A documentação do hibernate não explica como devemos utilizar as ferramentas e métodos. Depois de muitos testes e erros encontrei a solução para as temidas subqueries. E este post tem como objetivo esclarecer seu uso.
Pois bem, minha aplicação tem várias funções para os usuários. Sendo elas:
- Administradores
- Anunciantes
- Colaboradores
- Clientes
Então o usuário pode ter a função de administrador, anunciante, colaborador e/ou clientes. Contudo este não deve ser incluídos duas vezes para um função. Ou seja, caso adicione o usuário ANDRÉ NICOLAU como administrador não será permitido sua inclusão novamente e sim sua remoção da função.
Para verificar se o usuário já está em uma determinada função a única forma via SQL é utilizar subquery. Em SQL ficararia assim o comando:
SELECT *
FROM sys_usuarios s
WHERE s.id_usuario != ( SELECT a.id_usuario
FROM sys_administradores a );
E o resultado dessa query são os usuários que ainda não estão na tabela administradores. Agora como devemos fazer isso no hibernate?
Uma consulta simples no hibernate fazemos da seguinte maneira:
List list = getSeamSession().createCriteria( Usuarios.class ).list();
Onde:
getSeamSession() retorna a sessão do hibernate para então fazermos as manipulações no BD.
createCriteria( Usuarios.class ) se traduzirmos ao pé da letra significa “criar critérios”, então é a busca por critérios. Na documentação do hibernate explica legal como criar diversos critérios de busca.
list(); retorna o conjunto de dados da busca por critérios em uma lista.
O resultado em SQL do comando acima visto é esse:
Hibernate: select this_.id_usuario as id1_13_0_, this_.ativo as ativo13_0_, this_.mail as mail13_0_, this_.nome as nome13_0_, this_.senha as senha13_0_ from sys_usuarios
Um SELECT da vida!!!!
Agora como devemos fazer uma subquery? Simplesmente seguindo os procedimentos abaixo:
1 – Criar um objeto do tipo DetachedCriteria;
DetachedCriteria dc = DetachedCriteria.forClass( Administradores.class ).
setProjection( Property.forName( “idUsuario” ) );
Essa linha de comando anterior irá criar a subquery que ficará encapsulada na query principal.
Onde:
forClass( Administradores.class ) indica qual classe será utilizada na subquery.
setProjection( Property.forName( “idUsuario” ) ); indica qual campo da subquery será retornado.
2 – Criar a busca por critério e embutir a subquery criada antes;
List list = getSeamSession().createCriteria( Usuarios.class ).
add( Property.forName( “idUsuario” ).notIn( dc ) ).list();
Parece complicado! Mas se torna bem simples quando a gente entende cada método utilizado.
Onde:
List list = getSeamSession().createCriteria( Usuarios.class ) já vimos anteriormente como funciona.
add( Property.forName( “idUsuario” ) adiciona o campo da query principal que será comparada com aquele campo retornado da subquery.
notIn( dc ) é método de comparação. No comando SQL crú utilizamos != mas podemos também utilizar NOT IN no lugar. E temos o método notIn no hibernate para representa essa comparação e também o ne para representar o !=. E o dc é a nossa subquery criada anteriormente.
.list(); para retornar a lista do resultado.
Dessa forma, nosso código fica da seguinte maneira:
//Nossa subquery
DetachedCriteria dc = DetachedCriteria.forClass( Administradores.class ).
setProjection( Property.forName( “idUsuario” ) );
// Query principal e a subquery embutida
List list = getSeamSession().createCriteria( getObject().getClass() ).
add( Property.forName( “idUsuario” ).notIn( dc ) ).list();
O hibernate monta o SQL da seguinte forma:
Hibernate: select this_.id_usuario as id1_13_0_, this_.ativo as ativo13_0_, this_.mail as mail13_0_, this_.nome as nome13_0_, this_.senha as senha13_0_ from sys_usuarios this_ where this_.id_usuario not in (select this_.id_usuario as y0_ from sys_administradores this_)
Fazendo comparação com o comando SQL crú, a query do hibernate é similar.
A partir de então é só manipular a lista de resultados.
Abraços…
Dúvidas não exaltem em perguntas.