利用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,实现功能效果就好,哈哈

时间: 2024-10-25 23:00:41

利用Hibernate监听器实现用户操作日志的相关文章

基于NopCommerce的开发框架——缓存、网站设置、系统日志、用户操作日志

最近忙于学车,抽时间将Nop的一些公用模块添加进来,反应的一些小问题也做了修复.另外有园友指出Nop内存消耗大,作为一个开源电商项目,性能方面不是该团队首要考虑的,开发容易,稳定,代码结构清晰简洁也是很重要的一个方面,另外性能的优化也不仅仅是在框架层面.笔者在使用其开发一些中小型的项目,性能还在可以接受范围.前篇——基于nopCommerce的开发框架(附源码):http://www.cnblogs.com/dreling/p/6906688.html. 最新的代码已经同布到GitHub:htt

mysql颠覆实战笔记(三)-- 用户登录(二):保存用户操作日志的方法

版权声明:笔记整理者亡命小卒热爱自由,崇尚分享.但是本笔记源自www.jtthink.com(程序员在囧途)沈逸老师的<web级mysql颠覆实战课程 >.如需转载请尊重老师劳动,保留沈逸老师署名以及课程来源地址. 现在我们接着上节课,完成第二个功能,不管成功不成功都记录一个日志. 一.回顾上节课内容,我们在user_log表中添加一个字段 user_id.  在上一节课的存储过程新增一行代码,如下: BEGIN set @gid=0; set @user_name=''; set @_res

Android手机上,利用bat脚本模拟用户操作

---- 那么你就可以来看看这篇帖子了. 言归正传 利用bat脚本模拟用户操作,需要用到两点: ①就是adb命令了,adb命令可以用来模拟用户在手机上的操作 ②bat语言,就是批处理语言,主要用来进行逻辑处理,跟众多语言语法一样,批处理语言也包括for循环.if语句之类的语法: 一.adb命令 首先我们来介绍模拟用户在手机上操作的adb命令 input keyevent //发送键盘事件 用法说明: adb shell input keyevent "value" usage: inp

按时按登录IP记录Linux所有用户操作日志的方法(附脚本)

PS:Linux用户操作记录一般通过命令history来查看历史记录,但是如果因为某人误操作了删除了重要的数据,这种情况下history命令就不会有什么作用了.以下方法可以实现通过记录登陆IP地址和所有用户登录所操作的日志记录! 在/etc/profile配置文件的末尾加入以下脚本代码就可以实现,下面脚本是我网上找来的,原作者不知.但原脚本的时间变量有错误,不能记录时间,本人测试发现并检查修正: PS1="`whoami`@`hostname`:"'[$PWD]' history US

php记录用户操作日志的解决方案

b/s系统,客户要求系统要记录所有用户的操作(查看.修改.删除.添加等操作)功能 现在没有很好的方案来实现,不知哪位童鞋做过,给点思路 问题: 1.保存到数据库的话,数据量会非常大--肯定影响性能,不知有没有好的解决办法 2.保存到文件的话,是保存到一个文件中还是根据日期来保存到很多个文件中 如果保存到一个文件中也会有数据过大的问题 保存到很多文件的话如何实现根据不同用户检索操作日志以及分页显示问题 http://c.tieba.baidu.com/p/3393155246 http://c.t

简易的用户操作日志记录

记录系统内用户的操作日志. 1.日志类 public class SysLog { /** * 主键 */ private String id; /** * 操作开始时间 */ private Date createTime; /** * 操作结束时间 */ private Date endTime; /** * 执行耗时 */ private long excuteTime; /** * 接口地址 */ private String url; /** * 请求参数 */ private Str

log4j MDC用户操作日志追踪配置

https://blog.csdn.net/userwyh/article/details/52862216 一.MDC介绍 MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能.某些应用程序采用多线程的方式来处理多个用户的请求.在一个用户的使用过程中,可能有多个不同的线程来进行处理.典型的例子是 Web 应用服务器.当用户访问某个页面时,应用服务器可能会创建一个新的线程来处理该请求,也可能从线

syslog-ng+loganalyzer(非常详细配置文件)部署收集操作日志

一.syslog-ng 说明 二.syslog-ng 下载安装 三.syslog-ng 配置文件详细说明 四.配置syslog-ng收集操作日志 五.使用loganalyzer展示收集日志 1.syslog-ng说明 syslog-ng应用程序是一个灵活和高度可伸缩的系统日志应用程序创建集中的和可信的日志解决方案的理想选择.syslog-ng的主要特征概括如下: (1)可靠的日志传输:syslog-ng应用程序使您能够发送你的主机到远程服务器的日志消息使用最新的协议标准.不同服务器的日志可以收集

SSH基于Hibernate eventListener 事件侦听器的操作日志自动保存到数据库

在spring xml配置文件中添加配置,包含:model.listener 在model中增加需要写入数据库对应表的model 在auditLog.xml配置文件中配置自己项目中,需要进行日志记录的model类shortName,以及相关属性. 相关代码如下: 首先spring xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframew