网上搜索发现,实现用户操作日志的方式有:自定义注解方式、Hibernate拦截器方式、Hibernate监听器方式等。

1、自定义注解方式较为麻烦,需要进行操作记录的方法均需要添加注解,但是相对的操作描述更为针对性,缺点是无法获得所操作的实体ID以及成员;

2、拦截器方式经我自己试验,拦截器是在Hibernate操作数据库之前执行的,所以同样获取不了所操作的实体ID和成员,但是相对注解方式来说,不用在原有代码上更改添加注解等,耦合性比较低。

使用拦截器需要保证数据库操作均是对实体类的操作,即使用save、update、delete、get、load等方式,原生sql语句的执行是不会被拦截的;

3、监听器方式是我最终采用的方法,监听器是在Hibernate操作数据库之后执行的回调方式,可以获取操作实体的ID和成员变量,同样的相对业务层耦合性低,

使用监听器需要保证数据库操作均是对实体类的操作,即使用save、update、delete、get、load等方式,原生sql语句的执行是不会被拦截的。

下边展示我的代码:

这部分是监听器的注册部分

 import javax.annotation.PostConstruct;

 import org.hibernate.SessionFactory;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; /**
* hibernate的事件监听注册
* @author tianzhen
*/
@Component
public class HibernateEvent { @Autowired
private SessionFactory sessionFactory;
@Autowired
private OperListener operListener; @PostConstruct
public void registerListeners() {
EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(
EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(operListener);//对实体保存的监听
registry.getEventListenerGroup(EventType.POST_COMMIT_UPDATE).appendListener(operListener);//对实体修改的监听
registry.getEventListenerGroup(EventType.POST_COMMIT_DELETE).appendListener(operListener);//对实体删除的监听
}
}

这部分是监听器

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

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

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

    版权声明:笔记整理者亡命小卒热爱自由,崇尚分享.但是本笔记源自www.jtthink.com(程序员在囧途)沈逸老师的<web级mysql颠覆实战课程 >.如需转载请尊重老师劳动,保留沈逸 ...

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

    最近忙于学车,抽时间将Nop的一些公用模块添加进来,反应的一些小问题也做了修复.另外有园友指出Nop内存消耗大,作为一个开源电商项目,性能方面不是该团队首要考虑的,开发容易,稳定,代码结构清晰简洁也是 ...

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

    ………… 那么你就可以来看看这篇帖子了. 言归正传 利用bat脚本模拟用户操作,需要用到两点: ①就是adb命令了,adb命令可以用来模拟用户在手机上的操作 ②bat语言,就是批处理语言,主要用来进行 ...

  4. springAOP记录用户操作日志

    项目已经开发完成,需要加用户操作日志,如果返回去加也不太现实,所以使用springAOP来完成比较合适. 注解工具类: @Retention(RetentionPolicy.RUNTIME) @Tar ...

  5. 微软企业库5.0 学习之路——第九步、使用PolicyInjection模块进行AOP—PART4——建立自定义Call Handler实现用户操作日志记录

    在前面的Part3中, 我介绍Policy Injection模块中内置的Call Handler的使用方法,今天则继续介绍Call Handler——Custom Call Handler,通过建立 ...

  6. RabbitMQ实战场景(一):异步记录用户操作日志

    传统的项目开发中业务流程以串行方式,执行了模块1—>模块2–>模块3 而我们知道,这个执行流程其实对于整个程序来讲是有一定的弊端的,主要有几点: (1)整个流程的执行响应等待时间比较长; ...

  7. 我使用Spring AOP实现了用户操作日志功能

    我使用Spring AOP实现了用户操作日志功能 今天答辩完了,复盘了一下系统,发现还是有一些东西值得拿出来和大家分享一下. 需求分析 系统需要对用户的操作进行记录,方便未来溯源 首先想到的就是在每个 ...

  8. 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录

    在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...

  9. Spring AOP使用注解记录用户操作日志

    最后一个方法:核心的日志记录方法 package com.migu.cm.aspect; import com.alibaba.fastjson.JSON; import com.migu.cm.do ...

随机推荐

  1. 阿里云1218动态css3代码

    See the Pen jEWpWm by kujian (@kujian) on CodePen. .room-nav { /* -webkit-animation:roomNavTranslate ...

  2. 数据结构队列的java实现,包括线性和链式两种方式

    实现的思路为: 采用泛型的方式,首先定义了一个Queue的接口,然后通过实现该接口实现了线性和链式的两种形式的队列: 接口代码如下: package com.peter.java.dsa.interf ...

  3. IIS7.5下的httpModules设置

    部署到IIS7.5上httpModule不起作用了,原来是iis7里面的网站应用程序池使用了默认的“集成模式”,所以所有的http请求处理都托管给了 IIS,web.config里面的配置就不起效了. ...

  4. 移动web知识

    1.像素知识 px: css pixels,逻辑像素,浏览器使用的抽象单位 dp,pt:device independent pixels ,设备无关像素 dpr:devicePixelRatio 设 ...

  5. ubuntu下无法打开windows下ntfs文件系统的解决方法

    例如:/dev/sda5无法加载,使用如下命令 sudo ntfsfix /dev/sda5 如果命令没有安装,先安装(sudo apt-get install ntfsprogs).

  6. 企业架构研究总结(29)——TOGAF架构内容框架之概述及架构工作产品分类

    在TOGAF 9之前的版本中,TOGAF的重点主要集中在企业架构开发方法方面,用于指导其使用者如何在各自的组织中对企业架构进行创建和维护,而对于企业架构的具体内容并没有相关的论述,因而针对早期TOGA ...

  7. iOS 7用户界面过渡指南

    iOS 7用户界面过渡指南 泽涛陈 | 交互设计 视觉设计 译译生辉 | 2013.06.26 本文最新PDF格式文档下载: http://vdisk.weibo.com/s/InBpB(2013年7 ...

  8. TOGAF企业连续体和工具之企业连续体构成及架构划分

    TOGAF企业连续体和工具之企业连续体构成及架构划分 又回头看了之前文章的评论,本人也同样感慨这些文章的确像政治课本般的虚无缥缈,所以对费力看完却觉得无从下手的看官致以诚挚的歉意和理解,因为这个问题也 ...

  9. wp加载本地HTML(附带图片,CSS,JS)

    wp加载本地HTML(附带图片,CSS,JS) Windows Phone:Load Local HTML with Img,Css,Js by 唐小崇 http://www.cnblogs.com/ ...

  10. javascript ajax 脚本跨域调用全解析

    javascript ajax 脚本跨域调用全解析 今天终于有点时间研究了一下javsscript ajax 脚本跨域调用的问题,先在网上随便搜了一下找到一些解决的办法,但是都比较复杂.由是转到jqu ...