利用Hibernate监听器实现用户操作日志
网上搜索发现,实现用户操作日志的方式有:自定义注解方式、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监听器实现用户操作日志的更多相关文章
- mysql颠覆实战笔记(三)-- 用户登录(二):保存用户操作日志的方法
版权声明:笔记整理者亡命小卒热爱自由,崇尚分享.但是本笔记源自www.jtthink.com(程序员在囧途)沈逸老师的<web级mysql颠覆实战课程 >.如需转载请尊重老师劳动,保留沈逸 ...
- 基于NopCommerce的开发框架——缓存、网站设置、系统日志、用户操作日志
最近忙于学车,抽时间将Nop的一些公用模块添加进来,反应的一些小问题也做了修复.另外有园友指出Nop内存消耗大,作为一个开源电商项目,性能方面不是该团队首要考虑的,开发容易,稳定,代码结构清晰简洁也是 ...
- Android手机上,利用bat脚本模拟用户操作
………… 那么你就可以来看看这篇帖子了. 言归正传 利用bat脚本模拟用户操作,需要用到两点: ①就是adb命令了,adb命令可以用来模拟用户在手机上的操作 ②bat语言,就是批处理语言,主要用来进行 ...
- springAOP记录用户操作日志
项目已经开发完成,需要加用户操作日志,如果返回去加也不太现实,所以使用springAOP来完成比较合适. 注解工具类: @Retention(RetentionPolicy.RUNTIME) @Tar ...
- 微软企业库5.0 学习之路——第九步、使用PolicyInjection模块进行AOP—PART4——建立自定义Call Handler实现用户操作日志记录
在前面的Part3中, 我介绍Policy Injection模块中内置的Call Handler的使用方法,今天则继续介绍Call Handler——Custom Call Handler,通过建立 ...
- RabbitMQ实战场景(一):异步记录用户操作日志
传统的项目开发中业务流程以串行方式,执行了模块1—>模块2–>模块3 而我们知道,这个执行流程其实对于整个程序来讲是有一定的弊端的,主要有几点: (1)整个流程的执行响应等待时间比较长; ...
- 我使用Spring AOP实现了用户操作日志功能
我使用Spring AOP实现了用户操作日志功能 今天答辩完了,复盘了一下系统,发现还是有一些东西值得拿出来和大家分享一下. 需求分析 系统需要对用户的操作进行记录,方便未来溯源 首先想到的就是在每个 ...
- 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录
在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...
- Spring AOP使用注解记录用户操作日志
最后一个方法:核心的日志记录方法 package com.migu.cm.aspect; import com.alibaba.fastjson.JSON; import com.migu.cm.do ...
随机推荐
- Android项目---webView
之前用webView的时候,只知道它可以加载出html页面,竟然不知道,它也可以与js进行交互. WebView是一个网络视图,能加载显示网页,可以将它看作一个浏览器.它使用WebKit渲染引擎加载显 ...
- Delphi的注册表操作
转帖:Delphi的注册表操作 2009-12-21 11:12:52 分类: Delphi的注册表操作 32位Delphi程序中可利用TRegistry对象来存取注册表文件中的信息. 一.创 ...
- POCO Controller
---恢复内容开始--- POCO Controller 你这么厉害,ASP.NET vNext 知道吗? 写在前面 阅读目录: POCO 是什么? 为什么会有 POJO? POJO 的意义 PO ...
- NHibernate:教你如何搭建数据访问层?
NHibernate:教你如何搭建数据访问层? 什么是NHibernate NHibernate 是一个基于.net 的针对关系型数据库的对象持久化类库.NHibernate 来源于非常优秀的基于Ja ...
- 关于 MVCC 的基础
作为第一篇对 MVCC 的学习材料,以下内容翻译自 Wikipedia. 1. 什么是MVCC 1.1 基础概念 MVCC,Multi-Version Concurrency Control,多版本并 ...
- rebbitmq-RPC(C#)
RPC(Remote Procedure Call Protocol)——远程过程调用协议 运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步: 1.调用客户端句柄:执行传送参数 2.调 ...
- Day1:T3 bfs T4 树形DP
T3:BFS 回看了一下Day1的T3...感觉裸裸的BFS,自己当时居然没有看出来... 同时用上升和下降两种状态bfs即可 这一题还要注意一个细节的地方,就是题目要求的是求往返的最优解 k=min ...
- 一个人开发的html整站源码分享网站就这么上线了
项目我采用了纯静态html+动态搜索的模式,就是说详情页.主页等纯静态页面,仅搜索页面采用数据库访问搜索,搜索结果分为静态和动态,如果输入的关键字是已存在的标签就静态展示,否则就动态展示,这么做的好处 ...
- WCF MSMQ
基于WCF MSMQ 的企业应用解决方案 最近研究了一下基于MSMQ的WCF应用,从书上.网上查了很多资料,但始终没能彻底理解WCF-MSMQ的工作原理,也没能得到一个合理的应用解决方案.索性还是 ...
- C# IE代理操作
public class IPProxy { [System.Runtime.InteropServices.DllImport("wininet.dll", SetLastErr ...