Hibernate.orgCommunity Documentation
É muito útil quando a aplicação precisa reagir a certos eventos que ocorrem dentro do Hibernate. Isso permite a implementação de certas funções genéricas, assim como permite estender as funcionalidades do Hibernate.
A interface Interceptor
permite fornecer informações da sessão para o aplicativo, permitindo que o aplicativo inspecione e/ou manipule as propriedades de um objeto persistente antes de ser salvo, atualizado, excluído ou salvo. Pode ser usado para gerar informações de auditoria. Por exemplo, o seguinte Interceptor
ajusta a função automaticamente createTimestamp
quando um Auditable
é criado e atualiza a função lastUpdateTimestamp
quando um Auditable
é atualizado.
Você pode implementar Interceptor
diretamente ou pode estender EmptyInterceptor
.
package org.hibernate.test;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
public class AuditInterceptor extends EmptyInterceptor {
private int updates;
private int creates;
private int loads;
public void onDelete(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
// do nothing
}
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
updates++;
for ( int i=0; i < propertyNames.length; i++ ) {
if ( "lastUpdateTimestamp".equals( propertyNames[i] ) ) {
currentState[i] = new Date();
return true;
}
}
}
return false;
}
public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
loads++;
}
return false;
}
public boolean onSave(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if ( entity instanceof Auditable ) {
creates++;
for ( int i=0; i<propertyNames.length; i++ ) {
if ( "createTimestamp".equals( propertyNames[i] ) ) {
state[i] = new Date();
return true;
}
}
}
return false;
}
public void afterTransactionCompletion(Transaction tx) {
if ( tx.wasCommitted() ) {
System.out.println("Creations: " + creates + ", Updates: " + updates, "Loads: " + loads);
}
updates=0;
creates=0;
loads=0;
}
}
Os interceptadores se apresentam de duas formas: Session
-scoped e SessionFactory
-scoped.
Um interceptador delimitado da Session
, é definido quando uma sessão é aberta usando o método sobrecarregado da SessionFactory.openSession() que aceita um Interceptor
como parâmetro.
Session session = sf.openSession( new AuditInterceptor() );
Um interceptador da SessionFactory
-scoped é definido no objeto Configuration
antes da SessionFactory
ser instanciada. Nesse caso, o interceptador fornecido será aplicado para todas as sessões abertas por aquela SessionFactory
; Isso apenas não ocorrerá caso seja especificado um interceptador no momento em que a sessão for aberta. Um interceptador no escopo de SessionFactory
deve ser thread safe. Cetifique-se de não armazenar funções de estado específicos da sessão, pois, provavelmente, múltiplas sessões irão utilizar esse interceptador simultaneamente.
new Configuration().setInterceptor( new AuditInterceptor() );
Se você precisar executar uma ação em determinados eventos da camada de persistência, você também pode usar a arquitetura de event do Hibernate3. Um evento do sistema pode ser utilizado como complemento ou em substituição a um interceptador.
Essencialmente todos os métodos da interface Session
possuem um evento correlacionado. Se você tiver um LoadEvent
, um LoadEvent
, etc. Consulte o DTD do XML de arquivo deconfiguração ou o pacote org.hibernate.event
para a lista completa dos tipos de eventos). Quando uma requisição é feita em um desses métodos, a Session
do hibernate gera um evento apropriado e o envia para o listener de evento correspondente àquele tipo de evento. Esses listeners implementam a mesma lógica que aqueles métodos, trazendo os mesmos resultados. Entretanto, você é livre para implementar uma customização de um desses listeners (isto é, o LoadEvent
é processado pela implementação registrada da interface LoadEventListener
), então sua implementação vai ficar responsável por processar qualquer requisição load()
feita pela Session
.
Para todos os efeitos esses listeners devem ser considerados singletons. Isto significa que eles são compartilhados entre as requisições, e assim sendo, não devem salvar nenhum estado das variáveis instanciadas.
Um listener personalizado deve implementar a interface referente ao evento a ser processado e/ou deve estender a classes base equivalentes (ou mesmo os listeners padrões usados pelo Hibernate, eles não são declarados como finais com esse objetivo). O listener personalizado pode ser registrado programaticamente no objeto Configuration
, ou declarativamente no XML de configuração do Hibernate especificado. A configuração declarativa através do arquivo de propriedades não é suportado. Aqui temos um exemplo de como carregar um listener personalizado:
public class MyLoadListener implements LoadEventListener {
// this is the single method defined by the LoadEventListener interface
public void onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityClassName(), event.getEntityId() ) ) {
throw MySecurityException("Unauthorized access");
}
}
}
Você também precisa adicionar uma entrada no XML de configuração do Hibernate para registrar declarativamente qual listener deve se utilizado em conjunto com o listener padrão:
<hibernate-configuration>
<session-factory>
...
<event type="load">
<listener class="com.eg.MyLoadListener"/>
<listener class="org.hibernate.event.def.DefaultLoadEventListener"/>
</event>
</session-factory>
</hibernate-configuration
>
Ou, você pode registrar o listener programaticamente:
Configuration cfg = new Configuration();
LoadEventListener[] stack = { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners().setLoadEventListeners(stack);
Listeners registrados declarativamente não compartilham da mesma instância. Se o mesmo nome da classe for utilizado em vários elementos <listener/>
, cada um resultará em uma instância separada dessa classe. Se você tem a necessidade de compartilhar uma instância de um listener entre diversos tipos de listeners você deve registrar o listener programaticamente.
Mas, por quê implementar uma interface e definir o tipo específico durante a configuração? Bem, um listener pode implementar vários listeners de evento. Com o tipo sendo definido durante o registro, fica fácil ligar ou desligar listeners personalizados durante a configuração.
Geralmente a segurança declarativa nos aplicativos do Hibernate é gerenciada em uma camada de fachada de sessão. Agora o Hibernate3 permite certas ações serem aceitas através do JACC e autorizadas através do JAAS. Esta é uma funcionalidade opcional construída em cima da arquitetura do evento.
Primeiro, você precisa configurar um evento listener apropriado, para possibilitar o uso da autorização JAAS.
<listener type="pre-delete" class="org.hibernate.secure.JACCPreDeleteEventListener"/>
<listener type="pre-update" class="org.hibernate.secure.JACCPreUpdateEventListener"/>
<listener type="pre-insert" class="org.hibernate.secure.JACCPreInsertEventListener"/>
<listener type="pre-load" class="org.hibernate.secure.JACCPreLoadEventListener"/>
Note que <listener type="..." class="..."/>
é somente um atalho para <event type="..."><listener class="..."/></event>
quando existir somente um listener para um tipo de evento em particular.
Depois disso, ainda em hibernate.cfg.xml
, vincule as permissões aos papéis:
<grant role="admin" entity-name="User" actions="insert,update,read"/>
<grant role="su" entity-name="User" actions="*"/>
Os nomes das funções são as funções conhecidas pelo seu provedor JACC.
Copyright © 2004 Red Hat, Inc.