Na mnoha projektech je třeba řešit databázový žurnál aplikace, který zaznamenává události v systému včetně dat, která při těchto událostech byla změněna či jen čtena. Zákazníci často požadují u událostí, které mění data, žurnálovat jak staré tak i nové hodnoty záznamu. Implementace takového mechanismu přímo na DAO vrstvě by byla pracná a hlavně složitě konfigurovatelná. Řešení žurnálování na úrovni databázových procedur a triggerů by zase zrušilo nezávislost aplikace na konkrétním typu databáze. Hibernate pro žurnálování nabízí zajímavou funkčnost interceptor pro zachycení a zpracování různých událostí, jako například databázové CRUD operace.
Vycházím z příkladu použití interceptoru na audit log (žurnál) a samozřejmě ze samotné dokumentace http://docs.jboss.org/hibernate/core/3.3/reference/en/html/events.html Po menší modifikaci lze uvedený příklad použít i na žurnálování starých a nových hodnot. Vytvořímě si entitu AuditLogData pro ukládání změněných hodnot a přidáme ji do entity AuditLog.
@Entity @Table(name = "auditlog", catalog = "mkyong") 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; @OneToMany() @JoinColumn (name = "auditLogId") @OrderBy("propertyName") privateList auditLogData; ... }
@Entity @Table(name = "auditlogdata", catalog = "mkyong") public class AuditLogData implements java.io.Serializable { private Long auditLogDataId; private String propertyName; private String oldValue; private String newValue; ... }
Při ukládání záznamu budeme pracovat přímo se záznamem AuditLog třídy a ne pouze s entity.
public boolean onFlushDirty(Object entity,Serializable id, Object[] currentState,Object[] previousState, String[] propertyNames,Type[] types) throws CallbackException { ... if (entity instanceof IAuditLog){ AuditLog auditRecord = new AuditLog("Updated“,entity.getLogDeatil(), new Date(),entity.getId(), entity.getClass().toString()); fillChangedData(auditRecord, previousState, currentState, propertyNames, entity); //Add the auditLog to be saved as update after commit of transaction updates.add(auditRecord); } return false; }
Kde plnění změněných dat by mohlo vypadat přibližně následovně:
private void fillChangedData(AuditLog auditLog, Object[] startState, Object[] endState, String[] propertyNames, Object newObject, Long persistedObjectId) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { //check input parameters … for (int i = 0; i < propertyNames.length; i++) { //check if the property has been changed if (propertyValueChanged(startState[i], endState[i])) { // property changed, add it to auditLog.auditLogData list final String pn = propertyNames[i]; String valueOld = propertyValueToString(startState[i]); String valueNew = propertyValueToString(endState[i]); addDataToAuditLog(auditLog, pn, valueOld, valueNew); } } }
Jak je na příkladu vidět, Hibernate interceptor je mohutný nástroj, který umožňuje na aplikační úrovni nejen odchytávat operace nad databází, ale i dále zpracovávat jejich data.
Zpět