Пример перехватчика Hibernate - журнал аудита

Пример перехватчика гибернации - журнал аудита

Hibernate имеет мощную функцию, называемую «interceptor», для перехвата или перехвата различных событий Hibernate, таких как операция CRUD базы данных. В этой статье я продемонстрирую, как реализовать функцию журнала аудита приложений с помощью перехватчика Hibernate. Он будет регистрировать все операции сохранения, обновления или удаления Hibernate в таблице базы данных с именем «auditlog».

Пример перехватчика гибернации - журнал аудита

1. Создать таблицу

Создайте таблицу с именем «loglog »для хранения всех проверенных записей приложения.

DROP TABLE IF EXISTS `example`.`auditlog`;
CREATE TABLE  `example`.`auditlog` (
  `AUDIT_LOG_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `ACTION` varchar(100) NOT NULL,
  `DETAIL` text NOT NULL,
  `CREATED_DATE` date NOT NULL,
  `ENTITY_ID` bigint(20) unsigned NOT NULL,
  `ENTITY_NAME` varchar(255) NOT NULL,
  PRIMARY KEY (`AUDIT_LOG_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

2. Создать интерфейс маркера

Создайте интерфейс маркера, любые классы, которые реализовали этот интерфейс, будут проверяться. Этот интерфейс требует, чтобы реализованный класс предоставил ему идентификатор -getId() и содержимое для журнала - «getLogDeatil()». Все выставленные данные будут сохранены в базе данных.

package com.example.interceptor;
//market interface
public interface IAuditLog {

    public Long getId();
    public String getLogDeatil();
}

3. Сопоставьте таблицу «audlog»

Обычный файл модели аннотации для сопоставления с таблицей Auditlog.

@Entity
@Table(name = "auditlog", catalog = "example")
public class AuditLog implements java.io.Serializable {

    private Long auditLogId;
    private String action;
    private String detail;
    private Date createdDate;
    private long entityId;
    private String entityName;
        ...
}

4. Класс реализовал IAuditLog

Обычный файл модели аннотации для сопоставления с таблицей stock, который позже будет использован для демонстрации перехватчика. Он должен реализовать интерфейс маркераIAuditLog и реализовать методgetId() иgetLogDeatil().

...
@Entity
@Table(name = "stock", catalog = "example"
public class Stock implements java.io.Serializable, IAuditLog {
...
        @Transient
    @Override
    public Long getId(){
        return this.stockId.longValue();
    }

    @Transient
    @Override
    public String getLogDeatil(){
        StringBuilder sb = new StringBuilder();
        sb.append(" Stock Id : ").append(stockId)
        .append(" Stock Code : ").append(stockCode)
        .append(" Stock Name : ").append(stockName);

        return sb.toString();
    }
...

5. Создать вспомогательный класс

Вспомогательный класс, который принимает данные от перехватчика и сохраняет их в базе данных.

...
public class AuditLogUtil{

   public static void LogIt(String action,
     IAuditLog entity, Connection conn ){

     Session tempSession = HibernateUtil.getSessionFactory().openSession(conn);

     try {

    AuditLog auditRecord = new AuditLog(action,entity.getLogDeatil()
        , new Date(),entity.getId(), entity.getClass().toString());
    tempSession.save(auditRecord);
    tempSession.flush();

     } finally {
    tempSession.close();
     }
  }
}

6. Создать класс перехватчика Hibernate

Создайте класс перехватчика, расширяя HibernateEmptyInterceptor. Вот самая популярная функция перехватчика.

  • onSave - вызывается при сохранении объекта, объект еще не сохранен в базе данных.

  • onFlushDirty - Вызывается при обновлении объекта, объект еще не обновляется в базе данных.

  • onDelete - Вызывается, когда вы удаляете объект, объект еще не удален в базу данных.

  • preFlush - вызывается до того, как сохраненные, обновленные или удаленные объекты будут переданы в базу данных (обычно до postFlush).

  • postFlush - вызывается после фиксации сохраненных, обновленных или удаленных объектов в базе данных.

Код довольно многословен, он должен быть самоанализом.

...
public class AuditLogInterceptor extends EmptyInterceptor{

    Session session;
    private Set inserts = new HashSet();
    private Set updates = new HashSet();
    private Set deletes = new HashSet();

    public void setSession(Session session) {
        this.session=session;
    }

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

        System.out.println("onSave");

        if (entity instanceof IAuditLog){
            inserts.add(entity);
        }
        return false;

    }

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

        System.out.println("onFlushDirty");

        if (entity instanceof IAuditLog){
            updates.add(entity);
        }
        return false;

    }

    public void onDelete(Object entity, Serializable id,
        Object[] state, String[] propertyNames,
        Type[] types) {

        System.out.println("onDelete");

        if (entity instanceof IAuditLog){
            deletes.add(entity);
        }
    }

    //called before commit into database
    public void preFlush(Iterator iterator) {
        System.out.println("preFlush");
    }

    //called after committed into database
    public void postFlush(Iterator iterator) {
        System.out.println("postFlush");

    try{

        for (Iterator it = inserts.iterator(); it.hasNext();) {
            IAuditLog entity = (IAuditLog) it.next();
            System.out.println("postFlush - insert");
            AuditLogUtil.LogIt("Saved",entity, session.connection());
        }

        for (Iterator it = updates.iterator(); it.hasNext();) {
            IAuditLog entity = (IAuditLog) it.next();
            System.out.println("postFlush - update");
            AuditLogUtil.LogIt("Updated",entity, session.connection());
        }

        for (Iterator it = deletes.iterator(); it.hasNext();) {
            IAuditLog entity = (IAuditLog) it.next();
            System.out.println("postFlush - delete");
            AuditLogUtil.LogIt("Deleted",entity, session.connection());
        }

    } finally {
        inserts.clear();
        updates.clear();
        deletes.clear();
    }
       }
}

7.Enabling the interceptor

Вы можете включить перехватчик, передав его в качестве аргументаopenSession(interceptor);.

...
   Session session = null;
   Transaction tx = null;
   try {

    AuditLogInterceptor interceptor = new AuditLogInterceptor();
    session = HibernateUtil.getSessionFactory().openSession(interceptor);
    interceptor.setSession(session);

    //test insert
    tx = session.beginTransaction();
    Stock stockInsert = new Stock();
    stockInsert.setStockCode("1111");
    stockInsert.setStockName("example");
    session.saveOrUpdate(stockInsert);
    tx.commit();

    //test update
    tx = session.beginTransaction();
    Query query = session.createQuery("from Stock where stockCode = '1111'");
    Stock stockUpdate = (Stock)query.list().get(0);
    stockUpdate.setStockName("example-update");
    session.saveOrUpdate(stockUpdate);
    tx.commit();

    //test delete
    tx = session.beginTransaction();
    session.delete(stockUpdate);
    tx.commit();

   } catch (RuntimeException e) {
    try {
        tx.rollback();
    } catch (RuntimeException rbe) {
        // log.error("Couldn’t roll back transaction", rbe);
   }
    throw e;
   } finally {
    if (session != null) {
        session.close();
    }
   }
...

В тесте вставки

session.saveOrUpdate(stockInsert); //it will call onSave
tx.commit(); // it will call preFlush follow by postFlush

В обновлении теста

session.saveOrUpdate(stockUpdate); //it will call onFlushDirty
tx.commit(); // it will call preFlush follow by postFlush

В тесте удаления

session.delete(stockUpdate); //it will call onDelete
tx.commit();  // it will call preFlush follow by postFlush
Выход
onSave
Hibernate:
    insert into example.stock
    (STOCK_CODE, STOCK_NAME)
    values (?, ?)
preFlush
postFlush
postFlush - insert
Hibernate:
    insert into example.auditlog
    (ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME)
    values (?, ?, ?, ?, ?)
preFlush
Hibernate:
    select ...
    from example.stock stock0_
    where stock0_.STOCK_CODE='1111'
preFlush
onFlushDirty
Hibernate:
    update example.stock
    set STOCK_CODE=?, STOCK_NAME=?
    where STOCK_ID=?
postFlush
postFlush - update
Hibernate:
    insert into example.auditlog
    (ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME)
    values (?, ?, ?, ?, ?)
onDelete
preFlush
Hibernate:
    delete from example.stock where STOCK_ID=?
postFlush
postFlush - delete
Hibernate:
    insert into example.auditlog
    (ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME)
    values (?, ?, ?, ?, ?)
В базе данных
SELECT * FROM auditlog a;

Все проверенные данные вставляются в базу данных.

interceptor-example

Заключение

Журналы аудита - это полезная функция, которая часто обрабатывается в базе данных с помощью триггеров, но я бы порекомендовал использовать приложение для ее реализации в целях переносимости.

Загрузите этот пример -Hibernate interceptor example.zip