利用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 ...
随机推荐
- 【Oracle】-【ROWNUM与索引】-索引对ROWNUM检索的影响
看到ASK TOM的一篇文章,挺有感触的. http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:32812348052 ...
- ASP.NET MVC路由(5)
ASP.NET MVC路由(五) 前言 前面的篇幅讲解了MVC中的路由系统,只是大概的一个实现流程,让大家更清晰路由系统在MVC中所做的以及所在的位置,通过模糊的概念描述.思维导图没法让您看到路由的实 ...
- C#Console程序使用Ninject
本来想使用一下Ninject的,然后搜索了很久,都没找到比较详细的关于Ninject的使用方法等内容.于是乎干脆自己来写几篇介绍Ninject的内容. 1. 依赖注入和IOC 依赖注入和IO ...
- JS代码平面化
重构JS代码 - 让JS代码平面化 js中的嵌套函数用的很多,很牛叉,那为何要平面化? 易懂(自己及他人) 易修改(自己及他人) 平时Ajax调用写法(基于jQuery) $.post('url' ...
- 实现WebService的调用与被调用
之前一直用WCF来开发服务,可是从未用过WebService.对WebService有种很神奇的期待,都说WebService比较简单,但是从未用过就对我来说就是一种新的知识.起始让我来说WCF与We ...
- Java多线程学习笔记--生产消费者模式
实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...
- Android学习笔记-Intent(一)
Intent对象在Android官方API这样描述:It is a passive data structure holding an abstract description of an opera ...
- Asp.Net Web API 2(CRUD操作)第二课
Asp.Net Web API 2(CRUD操作)第二课 Asp.Net Web API 导航 Asp.Net Web API第一课:入门http://www.cnblogs.com/aehyok ...
- 关于sscanf函数的各种详细用法
看书的时候碰到sscanf函数,就上网查了很多资料,并加以自己的整理,希望对大家有所帮助. (因为参考的博客太多太散,就不一一注明,望大神们见谅) sscanf() :从一个字符串中读进与指定格式相 ...
- 使用反射机制实现jQuery调用ashx类中的指定方法
使用反射机制实现jQuery调用ashx类中的指定方法 近期用asp.net做个小网站,但又不喜欢使用asp.net的服务器端控件,经过一番思量后确定前端采用原始的html.后台采用Linq to ...