利用Hibernate監聽器實現用戶操作日志


網上搜索發現,實現用戶操作日志的方式有:自定義注解方式、Hibernate攔截器方式、Hibernate監聽器方式等。

1、自定義注解方式較為麻煩,需要進行操作記錄的方法均需要添加注解,但是相對的操作描述更為針對性,缺點是無法獲得所操作的實體ID以及成員;

2、攔截器方式經我自己試驗,攔截器是在Hibernate操作數據庫之前執行的,所以同樣獲取不了所操作的實體ID和成員,但是相對注解方式來說,不用在原有代碼上更改添加注解等,耦合性比較低。

使用攔截器需要保證數據庫操作均是對實體類的操作,即使用save、update、delete、get、load等方式,原生sql語句的執行是不會被攔截的;

3、監聽器方式是我最終采用的方法,監聽器是在Hibernate操作數據庫之后執行的回調方式,可以獲取操作實體的ID和成員變量,同樣的相對業務層耦合性低,

使用監聽器需要保證數據庫操作均是對實體類的操作,即使用save、update、delete、get、load等方式,原生sql語句的執行是不會被攔截的。

 

下邊展示我的代碼:

這部分是監聽器的注冊部分

 1 import javax.annotation.PostConstruct;
 2 
 3 import org.hibernate.SessionFactory;
 4 import org.hibernate.event.service.spi.EventListenerRegistry;
 5 import org.hibernate.event.spi.EventType;
 6 import org.hibernate.internal.SessionFactoryImpl;
 7 import org.springframework.beans.factory.annotation.Autowired;
 8 import org.springframework.stereotype.Component;
 9 
10 /** 
11  * hibernate的事件監聽注冊 
12  * @author tianzhen
13  */  
14 @Component  
15 public class HibernateEvent {  
16   
17     @Autowired
18     private SessionFactory sessionFactory;  
19     @Autowired  
20     private OperListener operListener;  
21       
22     @PostConstruct 
23     public void registerListeners() {  
24         EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(  
25                 EventListenerRegistry.class);  
26         registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(operListener);//對實體保存的監聽
27         registry.getEventListenerGroup(EventType.POST_COMMIT_UPDATE).appendListener(operListener);//對實體修改的監聽
28         registry.getEventListenerGroup(EventType.POST_COMMIT_DELETE).appendListener(operListener);//對實體刪除的監聽
29     }  
30 }

這部分是監聽器

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.http.HttpSession;

import org.hibernate.Session;
import org.hibernate.event.spi.PostCommitDeleteEventListener;
import org.hibernate.event.spi.PostCommitInsertEventListener;
import org.hibernate.event.spi.PostCommitUpdateEventListener;
import org.hibernate.event.spi.PostDeleteEvent;
import org.hibernate.event.spi.PostInsertEvent;
import org.hibernate.event.spi.PostUpdateEvent;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.Type;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.nuctech.model.Useroper;
import com.nuctech.util.Constant;

/** 
 * hibernate的事件監聽
 * @author tianzhen
 */  

@Component
public class OperListener implements PostCommitDeleteEventListener,PostCommitInsertEventListener,PostCommitUpdateEventListener{

    private static final long serialVersionUID = -4253791237768268101L;
    
    @Autowired
    private HttpSession httpSession;
    /**
     * 監聽修改事件
     */
    @Override
    public void onPostUpdate(PostUpdateEvent event) {
        StringBuffer des = new StringBuffer();//操作描述
        des.append("更新操作,更新內容{");
        String diff = arrayDiff(event.getState(), event.getOldState(), event.getPersister().getPropertyNames(), event.getPersister().getPropertyTypes());//判斷修改了哪些部分,並拼接成字符串
        des.append(diff);
        des.append("}");
        saveOperLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), des);
    }
    /**
     * 監聽插入事件
     */
    @Override
    public void onPostInsert(PostInsertEvent event) {
        if(!(event.getEntity() instanceof Useroper)){//當是對用戶操作表的插入時,不進行操作,否則進入死循環
            StringBuffer des = new StringBuffer();//操作描述
            des.append("新建操作,新建內容{");
            String inser = arrayToString(event.getState(), event.getPersister().getPropertyNames(), event.getPersister().getPropertyTypes());//判斷添加的哪些成員,並拼接成字符串
            des.append(inser);
            des.append("}");
            saveOperLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), des);
        }
    }
    /**
     * 監聽刪除事件
     */
    @Override
    public void onPostDelete(PostDeleteEvent event) {
        StringBuffer des = new StringBuffer();//操作描述
        des.append("刪除操作,刪除內容{");
        String del = arrayToString(event.getDeletedState(), event.getPersister().getPropertyNames(), event.getPersister().getPropertyTypes());//判斷刪除了哪些成員,並進行拼接
        des.append(del);
        des.append("}");
        saveOperLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), des);
    }


    
    /**
     * 日志的添加
     * @param session
     * @param des
     */
    private void saveOperLog(Session session, String tableName, Serializable targetId, StringBuffer des){
        int currUserId = (int) httpSession.getAttribute(Constant.CURRENT_USERID);//獲取操作人
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = sdf.format(date);//操作日期
        String sql = "INSERT useroper(UserID,TableName,TargetID,OperDesc,OperTime) VALUES("+currUserId+",'"+tableName+"',"+targetId+",'"+des.toString()+"','"+time+"')";
        Session logSession = session.getSessionFactory().openSession();//重新打開一個新的Hibernate session,並在使用完進行關閉,不可使用原session。
        logSession.createSQLQuery(sql).executeUpdate();
        logSession.close();
    }
    
    /**
     * 數組轉字符串
     * @param o 成員值
     * @param names 成員名
     * @param types 成員類型
     * @return
     */
    private String arrayToString(Object[] o, String[] names, Type[] types){
        StringBuffer result = new StringBuffer();
        for(int i=0;i<o.length;i++){
            if(types[i].isAssociationType())//外鍵忽略處理
                continue;
            result.append(names[i]+":"+o[i]+";");
        }
        if(result.toString().equals(""))
            result.append(";");
        return result.substring(0, result.length()-1);
    }
    
    /**
     * 數組不同部分轉字符串
     * @param n 成員新值
     * @param o 成員原值
     * @param names 成員名
     * @param types 成員類型
     * @return
     */
    private String arrayDiff(Object[] n, Object[] o, String[] names, Type[] types){
        StringBuffer result = new StringBuffer();
        //各參數數組均按序傳進來的,按index取值即可
        for(int i=0;i<n.length;i++){
            if(types[i].isAssociationType())//外鍵忽略處理
                continue;
            //如不相等,則加入字符串中
            if(!String.valueOf(n[i]).equals(String.valueOf(o[i]))){
                result.append(names[i]+":"+o[i]+">"+n[i]+";");
            }
        }
        return result.substring(0, result.length()-1);
    }
    @Override
    public boolean requiresPostCommitHanding(EntityPersister persister) {
        return true;
    }
    @Override
    public void onPostUpdateCommitFailed(PostUpdateEvent event) {
        
    }
    @Override
    public void onPostInsertCommitFailed(PostInsertEvent event) {
        
    }
    @Override
    public void onPostDeleteCommitFailed(PostDeleteEvent event) {
        
    }
}

我使用的監聽器接口均為PostCommitDeleteEventListener、PostCommitInsertEventListener、PostCommitUpdateEventListener,而不是PostDeleteEventListener、PostInsertEventListener、PostUpdateEventListener,前三者是業務邏輯對數據庫操作已經Commit后進行回調,后三者則不是,后三者在進行監聽時,雖然可以獲取各項值,但是在對值進行數據庫記錄時就會很麻煩,容易出現事物鎖等待超時的Bug,導致業務處理也不能完成,本人菜鳥沒有找到解決辦法,用的是前三者的接口,anyway,實現功能效果就好,哈哈


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM