Hibernate4 攔截器(Interceptor) 實現實體類增刪改的日誌記錄

開發應用程序的過程當中,常常會對一些比較重要的數據修改都須要寫日誌。在實際工做的工程中,這些數據都是存在表中的, 一個常見的作法是用觸發器,在增刪改的時候,用觸發器將數據寫入到另外一張表中去,但我的不推薦這麼作,緣由以下:
1. 若是有多個表,得寫不少觸發器。
2. 觸發器與數據庫特性關聯太緊,不一樣的數據庫,雖然思路同樣,但語法卻不太同樣。
對數據庫表操做的日誌記錄,徹底能夠利用Hibernate的Interceptor特性來實現,也就是攔截器。下面用一個具體的例子來講明如何使用Hibernate的Interceptor。

建立一個表,用來記錄日誌的表 java

程序代碼 程序代碼spring


Create TABLE  `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;sql



建立這個表對應的實體類:數據庫

程序代碼 程序代碼session



@Entity
@Table(name = "auditlog")
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;

    public AuditLog() {
    }

    public AuditLog(String action, String detail, Date createdDate,
            long entityId, String entityName) {
        this.action = action;
        this.detail = detail;
        this.createdDate = createdDate;
        this.entityId = entityId;
        this.entityName = entityName;
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "AUDIT_LOG_ID", unique = true, nullable = false)
    public Long getAuditLogId() {
        return this.auditLogId;
    }
        .... 餘下部分能夠參考提供下載的源代碼.app



建立一個接口,全部實現了這個接口的實體類,都會寫日誌ide

程序代碼 程序代碼工具


package com.mkyong.interceptor;

//market interface
public interface IAuditLog {    
    public Long getId();    
    public String getLogDeatil();
}post


這裏有兩個方法,getId,getLogDetail 須要實現類去實現具體的方法,也就是要被寫入到日誌表中的詳細記錄.

建立一個類實現了IAuditLog 接口,並給出接口方法的具體實現測試

程序代碼 程序代碼


@Entity
@Table(name="stock")
public class Stock implements java.io.Serializable,IAuditLog  {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="STOCK_ID")
    private Integer stockId;
    
    @Column(name="STOCK_CODE", length=10)
    private String stockCode;
    
    @Column(name="STOCK_NAME", length=20)
    private String stockName;

    public Stock() {
    }

    public Stock(String stockCode, String stockName) {
        this.stockCode = stockCode;
        this.stockName = stockName;
    }

    ....省略部分getter,setter。

    @Transient
    public Long getId(){
        return this.stockId.longValue();
    }
    
    @Transient
    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();
    }

}



建立記錄日誌的工具類,全部寫日誌公用 

程序代碼 程序代碼


public class AuditLogUtil{
    
    public static void LogIt(String action,
        IAuditLog entity){
        
        Session tempSession = HibernateUtil.getSessionFactory().openSession();
            
        try {
            tempSession.getTransaction().begin();
            AuditLog auditRecord = new AuditLog(action,entity.getLogDeatil()
                    , new Date(),entity.getId(), entity.getClass().toString());
            tempSession.save(auditRecord);
            tempSession.getTransaction().commit();
            
        } finally {    
            tempSession.close();
            
        }
            
    }
}



建立 Hibernate interceptor 攔截器,這是重點,這裏攔截全部須要記錄日誌的類,並處理 

程序代碼 程序代碼


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;
    }
        
    @Override
    public String onPrepareStatement(String sql) {
        System.out.println("execute sql: " + sql);
        return super.onPrepareStatement(sql);
    }

    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);
            }    
            
            for (Iterator it = updates.iterator(); it.hasNext();) {
                IAuditLog entity = (IAuditLog) it.next();
                System.out.println("postFlush - update");
                AuditLogUtil.LogIt("Updated",entity);
            }    
            
            for (Iterator it = deletes.iterator(); it.hasNext();) {
                IAuditLog entity = (IAuditLog) it.next();
                System.out.println("postFlush - delete");
                AuditLogUtil.LogIt("Deleted",entity);
            }    
            
        } finally {
            inserts.clear();
            updates.clear();
            deletes.clear();
        }
    }    
    
}


這裏面有幾個比較經常使用的方法:
onSave – 保存數據的時候調用,數據尚未保存到數據庫.
onFlushDirty – 更新數據時調用,但數據尚未更新到數據庫
onDelete – 刪除時調用.
preFlush – 保存,刪除,更新 在提交以前調用 (一般在 postFlush 以前).
postFlush – 提交以後調用(commit以後)

寫測試例子, 添加數據,更新數據,而後再刪掉

程序代碼 程序代碼


public class App {
    public static void main(String[] args) {

        Session session = null;
        Transaction tx = null;

        try {

            AuditLogInterceptor interceptor = new AuditLogInterceptor();
            
            session = HibernateUtil.getSessionFactory().withOptions().interceptor(interceptor).openSession();
            //session = HibernateUtil.getSessionFactory().openSession();
            //interceptor.setSession(session);
            
            //test insert
            tx = session.beginTransaction();
            Stock stockInsert = new Stock();
            stockInsert.setStockCode("1111");
            stockInsert.setStockName("yihaomen");
            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("yihaomen-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抰 roll back transaction", rbe);
            }
            throw e;
        } finally {
            if (session != null) {
                session.close();
            }
        }

    }

}



運行結果以下:

程序代碼 程序代碼


onSave
execute sql: insert into stock (STOCK_CODE, STOCK_NAME) values (?, ?)
Hibernate: insert into stock (STOCK_CODE, STOCK_NAME) values (?, ?)
preFlush
postFlush
postFlush - insert
Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)
preFlush
execute sql: select stock0_.STOCK_ID as STOCK_ID1_1_, stock0_.STOCK_CODE as STOCK_CO2_1_, stock0_.STOCK_NAME as STOCK_NA3_1_ from stock stock0_ where stock0_.STOCK_CODE='1111'
Hibernate: select stock0_.STOCK_ID as STOCK_ID1_1_, stock0_.STOCK_CODE as STOCK_CO2_1_, stock0_.STOCK_NAME as STOCK_NA3_1_ from stock stock0_ where stock0_.STOCK_CODE='1111'
preFlush
onFlushDirty
execute sql: update stock set STOCK_CODE=?, STOCK_NAME=? where STOCK_ID=?
Hibernate: update stock set STOCK_CODE=?, STOCK_NAME=? where STOCK_ID=?
postFlush
postFlush - update
Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)
onDelete
preFlush
execute sql: delete from stock where STOCK_ID=?
Hibernate: delete from stock where STOCK_ID=?
postFlush
postFlush - delete
Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)



另外查看 auditLog 這張表, 能夠看到日誌成功寫入


另外,若是是在SPRING 容器中使用,應該將這個interceptor 注入進去 

程序代碼 程序代碼


<bean id="hibermateInterceptor" class="com.yihaomen.interceptor.AuditLogInterceptor"/>  
      
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
        <property name="dataSource">  
            <ref local="dataSource_jdbc"/>  
        </property>  
        <property name="entityInterceptor" ref="hibermateInterceptor"/> 
        ..............
   </bean>



這樣就能實現對整個項目中須要記錄日誌的實體類進行攔截,並記錄增刪改的日誌記錄. 仍是很方便的,重點就是 Hibernate interceptor 的使用.

測試在是在 Hibernate 4.3 下測試的, 若是是hibernate 3 在openSession的時候是不一樣的,hibernate4用了session = HibernateUtil.getSessionFactory().withOptions().interceptor(interceptor).openSession(); 若是是Hibernate 3的話,應該是:session = HibernateUtil.getSessionFactory().openSession(interceptor);。

項目代碼下載:
下載文件 Hibernate4 interceptor audit log sample download

相關文章
相關標籤/搜索