As aplicações geralmente precisam apenas filtrar um pequeno conjunto de informações das tabelas da base de dados. Dessa forma, podemos criar filtros para eliminar uma informação desnecessária. Os filtros fornecem funções para que possamos limitar os resultados de uma consulta. Os filtros utilizam o mesmo conceito que as Views do SQL, no entanto, o Hibernate oferece um sistema de gerencialmente centralizado para os filtros.
Uma função interessante é que os filtros podem ser habilitados ou desabilitados durante uma sessão do Hibernate. Além disso, os filtros podem ser parametrizados, o que é útil em aplicações mais acima da camada do Hibernate que usam segurança ou personalização.
No restante do artigo estaremos verificando melhor o que são os filtros do Hibernate e o que ele oferece aos desenvolvedores Hibernate.
Vantagens e Desvantagens
A vantagem de utilizarmos os filtros é que eles podem ser ligados ou desligados programaticamente, além disso, os filtros são definidos nos documentos de mapeamento do Hibernate, facilitando assim a manutenibilidade. A desvantagem é que não é permitido criar novos filtros em tempo de execução.
Apesar da definição dos filtros precisarem ser definidos em documentos de mapeamento do Hibernate, o que pode parecer uma limitação, os filtros podem ser parametrizados tornando-os assim bastante flexíveis.
Como um exemplo, poderíamos imaginar vários usuários num sistema que estamos desenvolvendo. No entanto, o cliente do nosso software precisa que agora apenas usuários ativos sejam considerados nas listagens. Para fazer isso de uma maneira bastante simples, poderíamos ter um filtro em que o status seria recebido por parâmetros e dessa forma poderíamos limitar os resultados.
É bom enfatizarmos que aplicações sem filtros podem ser construídas, no entanto, os filtros são excelentes alternativas para certos tipos de problemas como segurança e personalização.
Definindo Filtros
A primeira coisa a ser feita é definirmos os filtros para a nossa aplicação. Podemos usar tanto anotações quanto documentos de mapeamento XML. A definição do filtro tem o nome do filtro, um nome e um tipo para cada parâmetro que o filtro usa. Os parâmetros dos filtros são similares aos parâmetros nomeados (named queries) das consultas HQL. Ambos requerem um ":" antes do nome do parâmetro.
Cada classe ou coleção que queremos adicionar um filtro precisa ser adicionado através de uma anotação ou um elemento XML conforme será visto posteriormente.
Também podemos ter mais que um filtro para cada definição de filtro, além disso, cada classe pode ter mais que um filtro. Apesar de parecer um pouco confuso esse alto nível de abstração o entendimento ficará mais simples nas próximas seções.
Criando Filtros utilizando Anotações
Para utilizar filtros com anotações, precisamos usar as anotações @FilterDef, @ParamDef, e @Filter. A anotação @FilterDef define o filtro e pertence à classe ou ao pacote. Para adicionar um filtro numa classe devemos adicionar uma anotação @FilterDef depois da anotação @Entity. O exemplo abaixo demonstra o uso da anotação:
@Entity
@FilterDef(name="ultimoPagamentoFiltro",
parameters=@ParamDef( name="data", type="date" ) )
Agora que definimos nossos filtros, podemos anexa-los às nossas classes ou coleções utilizando anotação @Filter. A anotação @Filter tem dois parâmetros: name e condition. O primeiro parâmetro, chamado name, referencia uma definição de filtro que foi anteriormente descrito em uma anotação. O segundo parâmetro, chamado condition, é uma cláusula WHERE do HQL. Os parâmetros no condition são indicados com dois pontos, muito similar ao que ocorre com os parâmetros nomeados no HQL. Os parâmetros precisam ser definidos na definição do filtro. Segue abaixo um exemplo utilizando a anotação @Filter:
@Filter(name="ultimoPagamentoFiltro", condition=":data = dataDoPagamento")
Criando Filtros utilizando XML
Nos documentos de mapeamento XML, usamos o elemento <filter-def>. Essas definições de filtros devem conter o nome do filtro e os nomes e tipos dos parâmetros dos filtros. Especificamos os parâmetros do filtro com o elemento XML <filter-param>. Na Listagem 1 temos um exemplo com um filtro chamado ultimoPagamentoFiltro.
Listagem 1. Criação do filtro
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class ...
</class>
<filter-def name="ultimoPagamentoFiltro">
<filter-param name="data" type="date"/>
</filter-def>
</hibernate-mapping>
Depois que criamos a definição dos filtros precisamos anexa-los às nossas classes ou coleções. Para isso utilizamos o elemento XML <filter> para cada classe ou coleção. O elemento <filter> tem dois atributos: name e condition. O name referencia uma definição de filtro (como, por exemplo, ultimoPagamentoFiltro). A cláusula WHERE representa uma condição em HQL. Segue um exemplo:
<class ...
<filter name="ultimoPagamentoFiltro"
condition=":data = dataPagamento"/>
</class>
Cada elemento <filter> deve corresponder a um elemento <filter-def>.
Utilizando Filtros na Prática
Nossa aplicação deve determinar programaticamente se os filtros serão ativados ou desativados para uma determinada session do Hibernate. Cada sessão pode ter diferentes conjuntos de filtros com diferentes valores de parâmetros. Por padrão, as sessões não tem nenhum filtro ativo, ou seja, devemos explicitamente habilitar os filtros programaticamente para cada sessão. A interface Session contém vários métodos para que possamos trabalhar com filtros, são eles:
- public Filter enableFilter(String filterName): Ativa o filtro especificado.
- public Filter getEnabledFilter(String filterName): Retorna um filtro já ativado.
- public void disableFilter(String filterName): Desativa o filtro especificado.
A interface org.hibernate.Filter possui seis métodos. O método validate() muito dificilmente será utilizado, Hibernate usa este método quando ele processa os filtros. Outros cinco métodos são os seguintes:
- public Filter setParameter(String name, Object value): Este método permite que possamos substituir qualquer objeto Java para o parâmetro, embora este tipo deva ser compatível com o tipo especificado para o parâmetro quando o filtro foi definido.
- public Filter setParameterList(String name, Collection values): Muito útil para usar cláusulas IN nos filtros. Se quisermos usar cláusulas BETWEEN, usamos dois parâmetros de filtros diferentes com nomes diferentes.
- public Filter setParameterList(String name, Object[] values): O mesmo que o de cima mas utiliza arrays para os valores.
- public String getName(): Retorna o nome do filtro.
- public FilterDefinition getFilterDefinition(): permite que possamos retornar um objeto FilterDefinition representando o metadata (nome do filtro, nome dos parâmetros e seus tipos) do filtro.
Exemplo Utilizando Filtros
Como um exemplo inicial, temos na Listagem 2 um filtro definido que é chamado activatedFilter. Esse filtro foi criado num documento de mapeamento XML do Hibernate chamado User.hbm.xml. Os parâmetros do filtro devem ser especificados com o elemento XML chamado <filter-param> que usa um elemento XML <activatedParam>. Os tipos também são definidos.
Após definirmos os filtros precisamos anexá-los à uma classe. No final da classe User especificamos um filtro com o nome activatedFilter. Também precisamos setar uma condição que corresponde a uma cláusula WHERE do HQL para o filtro anexado. No nosso caso, usamos “:activatedParam = activated”, onde :activatedParam é o nome do parâmetro especificado na definição do filtro, e activated é o nome da coluna da tabela do usuário.
Listagem 2. Filtro activatedFilter
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.exemplo.filters.User">
<id name="id" type="int">
<generator class="native"/>
</id>
<property name="username" type="string" length="32"/>
<property name="ativo" type="boolean"/>
<filter name="filtroAtivo" condition=":parametroAtivo = ativo"/>
</class>
<filter-def name=" filtroAtivo ">
<filter-param name="parametroAtivo" type="boolean"/>
</filter-def>
</hibernate-mapping>
Abaixo segue a definição da classe User (Listagem 3):
Listagem 3. Classe User
package com.exemplo.filters;
public class User {
private int id;
private String username;
private boolean ativo;
public boolean isAtivo() {
return ativo;
}
public void setAtivo(boolean ativo) {
this.ativo = ativo;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Agora que a definição do filtro está criada e anexada à classe através de uma condição (:parametroAtivo = ativo), devemos ativar os filtros. A classe definida abaixo chamada ExemploFiltro, insere vários registros de usuários na base de dados e de imediato mostra eles na tela. A classe usa uma simples consulta HQL (from User). O método displayUsers() escreve os nomes de usuários (usernames) e o status de ativação no console. Antes de habilitarmos os filtros na base de dados esse método mostrará todos os usuários da base de dados. Após habilitar o primeiro filtro (filtroAtivo) para mostrar apenas os usuários ativos, chamaremos novamente o método displayUsers(). O resultado da consulta são os mesmos como se tivéssemos adicionado uma cláusula WHERE contendo a condição ativo=true após o from User do HQL. Porém, utilizando filtros podemos simplesmente alterar o valor do parâmetro do filtro e facilmente podemos mostrar, por exemplo, usuários inativos. Observe a Listagem 4.
Listagem 4. Ativando filtros
package com.exemplo.filters;
import java.util.Iterator;
import org.hibernate.Filter;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class ExemploFiltro {
public static void main (String args[]) {
SessionFactory factory =
new Configuration().configure().buildSessionFactory();
Session session = factory.openSession();
//insere os usuarios
insertUser("Cleber",true,session);
insertUser("Joao",true,session);
insertUser("Camila",false,session);
insertUser("Julia",false,session);
insertUser("Rafael",false,session);
//Mostra todos os Usuarios inseridos Acima
System.out.println("--Mostrando Todos os Usuarios--");
mostraUsuarios(session);
//Somente mostra os Usuarios ATIVOS
Filter filter = session.enableFilter("filtroAtivo");
filter.setParameter("parametroAtivo",new Boolean(true));
System.out.println("--Mostrando Todos os Usuarios Ativos--");
mostraUsuarios(session);
// Somente mostra os Usuarios INATIVOS
filter.setParameter("parametroAtivo",new Boolean(false));
System.out.println("--Mostrando Todos
os Usuarios Inativos--");
mostraUsuarios(session);
session.close();
}
public static void mostraUsuarios(Session session) {
session.beginTransaction();
Query query = session.createQuery("from User");
Iterator results = query.iterate();
while (results.hasNext()) {
User user = (User) results.next();
System.out.print(user.getUsername() + " is ");
if (user.isActivated()) {
System.out.println("ativo.");
} else {
System.out.println("não ativo.");
}
}
session.getTransaction().commit();
}
public static void insertUser(String name,
boolean ativo, Session session) {
session.beginTransaction();
User user = new User();
user.setUsername(name);
user.setAtivo(ativo);
session.save(user);
session.getTransaction().commit();
}
}
Segue abaixo a saída da execução desta classe (Listagem 5):
Listagem 5. Saída da execução
--Mostrando Todos os Usuarios--
Cleber ativo.
Joao não ativo.
Camila não ativo.
Julia não ativo.
Rafael não ativo.
--Mostrando Todos os Usuarios Ativos--
Cleber ativo.
Joao ativo.
--Mostrando Todos os Usuarios Inativos--
Camila não ativo.
Julia não ativo.
Rafael não ativo.
Os filtros não utilizam quaisquer informações relativas a banco de dados, dessa forma, podemos rodar este exemplo em qualquer base de dados sem nenhum problema. O arquivo de configuração do Hibernate define configurações da base de dados e informações sobre a conexão, além do documento de mapeamento XML para a classe User. Veja a Listagem 6.
Listagem 6. Arquivo de configuração
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
org.hsqldb.jdbcDriver
</property>
<property name="hibernate.connection.url">
jdbc:hsqldb:file:filterdb;SHUTDOWN=true
</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.pool_size">0</property>
<property name="dialect">
org.hibernate.dialect.HSQLDialect
</property>
<!-- Mapping files -->
<mapping resource="com/exemplo/filters/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Assim, neste artigo vimos o que são os filtros, quais as suas vantagens e desvantagens. Também vimos como podemos criar uma aplicação de exemplo definindo e utilizando os filtros.
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] Jeff Linwood and Dave Minter. An introduction to persistence using Hibernate 3.5, Second Edition. Apress.
[4] Steve Perkins. Hibernate Search by Example: Explore the Hibernate Search system and use its extraordinary search features in your own applications. Packt Publishing.