利用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 ...
随机推荐
- java中数据库通用层
/** * 数据库通用类 * */ public class ConnDB { /** * 获取数据库连接对象 * @return 数据库连接对象 * */ public static Connect ...
- JavaScript实例技巧精选(14)—动态变化背景颜色
>>点击这里下载完整html源码<< 这是截图: 网页背景颜色随时间变化,核心代码如下: <SCRIPT LANGUAGE="JavaScript"& ...
- [Usaco2007 Dec]穿越泥地[bfs][水]
Description 清早6:00,Farmer John就离开了他的屋子,开始了他的例行工作:为贝茜挤奶.前一天晚上,整个农场刚经受过一场瓢泼大雨的洗礼,于是不难想见,FJ 现在面对的是一大片泥泞 ...
- 基于ASP.NET MVC的热插拔模块式开发框架(OrchardNoCMS)介绍(二)
基于ASP.NET MVC的热插拔模块式开发框架(OrchardNoCMS)介绍(二) 之前文章中给大家说明了下我这个小小的想法,发现还是有不少人的支持和关注.你们的鼓励是对我最大的支持. 我总结了了 ...
- 教你使用shell数组
数组的使用,需要掌握 1.对数组进行赋值 2.通过下标访问数组元素 3.循环遍历所有的元素 代码如下: #!/bin/bash a="39" b="5" c=& ...
- Memcache Slab Eviction 功能测试
Memcache Slab Eviction 功能测试 1 功能简述 1.1 Automove功能背景 由于memcache的内存分配是基于slab的,每个1M的page内只能存放对应slab大小范围 ...
- journaling保证意外故障下的数据完整性
mongoDB研究笔记:journaling保证意外故障下的数据完整性 mongoDB的Journaling日志功能与常见的log日志是不一样的,mongoDB也有log日志,它只是简单记录了数据 ...
- cocos2dx android lua文件设置问题
cocos2d-x版本: cocos2d-2.1rc0-x-2.1.2 通常我们在文件AppDelegate.cpp的applicationDidFinishLaunching()函数中设置lua代码 ...
- java正则表达式验证汉字
统计指定内容的汉字个数: String str = "北京欢迎你 hello welcome!"; int count=0; Pattern pattern = Pattern.c ...
- nginx-push-stream模块源码学习(三)——发布
一.概述 发布:发布者将MSG post到某一特定通道上,channel将信息缓存 在说明发布流程之前有必要说明下channel和msg的数据结构. 二.数据结构 2.1 MSG 发布 ...