Exemple d'intercepteur Hibernate - journal d'audit
Hibernate possède une fonctionnalité puissante appelée «interceptor» pour intercepter ou accrocher différents types d'événements Hibernate, comme l'opération CRUD de base de données. Dans cet article, je vais montrer comment implémenter une fonctionnalité de journal d’audit d’applications à l’aide de l’intercepteur Hibernate, il consignera toutes les opérations d’enregistrement, de mise à jour ou de suppression d’Hibernate dans une table de base de données nommée «auditlog».
Exemple d'intercepteur Hibernate - journal d'audit
1. Créer une table
Créez une table appelée ‘auditlog’ pour stocker tous les enregistrements audités de l’application.
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. Créer une interface de marqueur
Créez une interface marqueur, toutes les classes qui ont implémenté cette interface seront auditées. Cette interface requiert que la classe implémentée expose son identifiant -getId() et le contenu à journaliser - «getLogDeatil()». Toutes les données exposées seront stockées dans la base de données.
package com.example.interceptor; //market interface public interface IAuditLog { public Long getId(); public String getLogDeatil(); }
3. Mappez la table ‘auditlog’
Un fichier de modèle d'annotation normal à mapper avec la table «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. Une classe a implémenté IAuditLog
Un fichier de modèle d'annotation normal à mapper avec la table «stock», qui sera utilisé pour la démonstration d'intercepteur plus tard. Il doit implémenter l'interface de marqueurIAuditLog et implémenter les méthodesgetId() etgetLogDeatil().
... @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. Créer une classe d'assistance
Une classe d'assistance pour accepter les données de l'intercepteur et les stocker dans la base de données.
... 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. Créer une classe d'intercepteur Hibernate
Créez une classe d'intercepteur en étendant lesEmptyInterceptor Hibernate. Voici la fonction d'intercepteur la plus populaire.
-
onSave - Appelé lorsque vous enregistrez un objet, l'objet n'est pas encore enregistré dans la base de données.
-
onFlushDirty - Appelé lorsque vous mettez à jour un objet, l'objet n'est pas encore mis à jour dans la base de données.
-
onDelete - Appelé lorsque vous supprimez un objet, l'objet n'est pas encore supprimé dans la base de données.
-
preFlush - Appelé avant que les objets enregistrés, mis à jour ou supprimés ne soient validés dans la base de données (généralement avant postFlush).
-
postFlush - Appelé après la validation des objets enregistrés, mis à jour ou supprimés dans la base de données.
Le code est assez verbeux, il devrait être auto-exploratoire.
... 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
Vous pouvez activer l'intercepteur en le transmettant comme argument à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(); } } ...
En test d'insertion
session.saveOrUpdate(stockInsert); //it will call onSave tx.commit(); // it will call preFlush follow by postFlush
En test de mise à jour
session.saveOrUpdate(stockUpdate); //it will call onFlushDirty tx.commit(); // it will call preFlush follow by postFlush
En test de suppression
session.delete(stockUpdate); //it will call onDelete tx.commit(); // it will call preFlush follow by postFlush
Sortie
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 (?, ?, ?, ?, ?)
Dans la base de données
SELECT * FROM auditlog a;
Toutes les données auditées sont insérées dans la base de données.
Conclusion
Les journaux d'audit sont une fonctionnalité utile qui est souvent gérée dans la base de données à l'aide de déclencheurs, mais je recommanderais d'utiliser l'application pour l'implémenter pour des raisons de portabilité.
Téléchargez cet exemple -Hibernate interceptor example.zip