Interceptores de hibernação

Interceptores de hibernação

1. Visão geral

Nesta discussão, veremos várias maneiras de interceptar operações dentro da implementação de mapeamento relacional abstraído deHibernate’s.

2. Definindo Interceptadores Hibernate

O Hibernate Interceptor é uma interface que nos permite reagir a certos eventos dentro do Hibernate.

Esses interceptores são registrados como callbacks e fornecem links de comunicação entre a sessão do Hibernate e o aplicativo. Com esse retorno de chamada, um aplicativo pode interceptar as principais operações do Hibernate, comosave, update, delete, etc.

Existem duas maneiras de definir interceptores:

  1. interfaceimplementing the org.hibernate.Interceptor

  2. extending the org.hibernate.EmptyInterceptor classe

2.1. Implementando uma interfaceInterceptor

Implementing org.hibernate.Interceptor requires implementing about 14 accompanying methods. Esses métodos incluemonLoad, onSave, onDelete, findDirty,e alguns mais.

Também é importante garantir que qualquer classe que implemente a interface do Interceptor seja serializável (implements java.io.Serializable).

Um exemplo típico seria semelhante a:

public class CustomInterceptorImpl implements Interceptor, Serializable {

    @Override
    public boolean onLoad(Object entity, Serializable id,
      Object[] state, String[] propertyNames, Type[] types)
      throws CallbackException {
        // ...
        return false;
    }

    // ...

    @Override
    public String onPrepareStatement(String sql) {
        // ...
        return sql;
    }

}

Se não houver requisitos especiais, a classeextending the EmptyInterceptor e apenas substituir os métodos necessários é altamente recomendado.

2.2. EstendendoEmptyInterceptor

Estender a classeorg.hibernate.EmptyInterceptor fornece uma maneira mais fácil de definir um interceptor. We now only need to override the methods that relate to the operation we want to intercept.

Por exemplo, podemos definir nossoCustomInterceptor como:

public class CustomInterceptor extends EmptyInterceptor {
}

E se precisarmos interceptar as operações de salvamento de dados antes de serem executadas, precisamos substituir o métodoonSave:

@Override
public boolean onSave(Object entity, Serializable id,
  Object[] state, String[] propertyNames, Type[] types) {

    if (entity instanceof User) {
        logger.info(((User) entity).toString());
    }
    return super.onSave(entity, id, state, propertyNames, types);
}

Observe como essa implementação simplesmente imprime a entidade - se for umUser.

Embora seja possível retornar um valor detrue oufalse, é uma boa prática permitir a propagação do eventoonSave invocandosuper.onSave().

Another use-case would be providing an audit trail for database interactions. Podemos usar o métodoonFlushDirty() para saber quando uma entidade muda.

Para o objetoUser, podemos decidir atualizar sua propriedade de datalastModified sempre que ocorrerem alterações nas entidades do tipoUser.

Isso pode ser alcançado com:

@Override
public boolean onFlushDirty(Object entity, Serializable id,
  Object[] currentState, Object [] previousState,
  String[] propertyNames, Type[] types) {

    if (entity instanceof User) {
        ((User) entity).setLastModified(new Date());
        logger.info(((User) entity).toString());
    }
    return super.onFlushDirty(entity, id, currentState,
      previousState, propertyNames, types);
}

Outros eventos comodeleteeload (inicialização do objeto) podem ser interceptados implementando os métodosonDeleteeonLoad correspondentes, respectivamente.

3. RegistrandoInterceptors

Um interceptor do Hibernate pode ser registrado comoSession-escopo ouSessionFactory-scoped.

3.1. Session-scoped Interceptor

Um interceptor com escopoSession está vinculado a uma sessão específica. É criado quando a sessão está sendo definida ou aberta como:

public static Session getSessionWithInterceptor(Interceptor interceptor)
  throws IOException {
    return getSessionFactory().withOptions()
      .interceptor(interceptor).openSession();
}

Acima, registramos explicitamente um interceptador em uma sessão de hibernação específica.

3.2. SessionFactory-escopoInterceptor

Um interceptor com escopoSessionFactory-é registrado antes de construir umSessionFactory.. Isso normalmente é feito por meio do métodoapplyInterceptor em uma instânciaSessionFactoryBuilder:

ServiceRegistry serviceRegistry = configureServiceRegistry();
SessionFactory sessionFactory = getSessionFactoryBuilder(serviceRegistry)
  .applyInterceptor(new CustomInterceptor())
  .build();

É importante observar que um interceptor com escopoSessionFactory-erá aplicado a todas as sessões. Portanto, precisamos ter cuidado para não armazenar o estado específico da sessão - pois esse interceptador será usado por diferentes sessões simultaneamente.

Para um comportamento específico de sessão, é recomendado abrir explicitamente uma sessão com um interceptor diferente, conforme mostrado anteriormente.

Para interceptores com escopoSessionFactory, naturalmente precisamos garantir que ele seja seguro para thread. Isso pode ser alcançado especificando um contexto de sessão no arquivo de propriedades:

hibernate.current_session_context_class=org.hibernate.context.internal.ThreadLocalSessionContext

Ou adicionando isso ao nosso arquivo de configuração XML:


    org.hibernate.context.internal.ThreadLocalSessionContext

Além disso, para garantir a serialização, os interceptores com escopoSessionFactory devem implementar o métodoreadResolve da interfaceSerializable.

4. Conclusão

Vimos como definir e registrar os interceptores do Hibernate comoSession-scoped ouSessionFactory-scoped. Em qualquer um dos casos, devemos garantir que os interceptores sejam serializáveis, especialmente se queremos uma sessão serializável.

Outras alternativas para interceptores incluem eventos de hibernação e retornos de chamada JPA.

E, como sempre, você pode verificar osource code over on Github completo.