Hibernate.org Community Documentation

- Anterior
- Próxima

## Capítulo 14. Interceptadores e Eventos

É 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.

## 14.1. Interceptadores

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() );

## 14.2. Sistema de Eventos

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.

## 14.3. Segurança declarativa do Hibernate

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.

- AnteriorCapítulo 13. Transações e Concorrência
- Acima
- Principal
- PróximaCapítulo 15. Batch processing