zoukankan      html  css  js  c++  java
  • 利用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,实现功能效果就好,哈哈

  • 相关阅读:
    四轴无人机原理
    python中同步、多线程、异步IO、多线程对IO密集型的影响
    匿名四轴上位机使用手册
    python异步编程--回调模型(selectors模块)
    Python多线程异步任务队列
    jenkins配置邮箱服务器(126邮箱)
    We wanted {"required":["value"]} and you sent ["text","value","id","sessionId"]
    Updates were rejected because the remote contains work that you do
    Git更新代码
    Git 从github克隆文件至本地
  • 原文地址:https://www.cnblogs.com/tianyuchen/p/5957298.html
Copyright © 2011-2022 走看看