Hibernate4 拦截器(Interceptor) 实现实体类增删改的日志记录
转自:https://blog.csdn.net/he90227/article/details/44783099
开发应用程序的过程中,经常会对一些比较重要的数据修改都需要写日志。在实际工作的工程中,这些数据都是存在表中的, 一个常见的做法是用触发器,在增删改的时候,用触发器将数据写入到另一张表中去,但个人不推荐这么做,原因如下:
1. 如果有多个表,得写很多触发器。
2. 触发器与数据库特性关联太紧,不同的数据库,虽然思路一样,但语法却不太一样。
对数据库表操作的日志记录,完全可以利用Hibernate的Interceptor特性来实现,也就是拦截器。下面用一个具体的例子来说明如何使用Hibernate的Interceptor。
创建一个表,用来记录日志的表
- Create TABLE `auditlog` (
- `AUDIT_LOG_ID` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
- `ACTION` VARCHAR(100) NOT NULL,
- `DETAIL` text NOT NULL,
- `CreateD_DATE` DATE NOT NULL,
- `ENTITY_ID` BIGINT(20) UNSIGNED NOT NULL,
- `ENTITY_NAME` VARCHAR(255) NOT NULL,
- PRIMARY KEY (`AUDIT_LOG_ID`)
- ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
创建这个表对应的实体类:
- @Entity
- @Table(name = "auditlog")
- public class AuditLog implements java.io.Serializable {
- private Long auditLogId;
- private String action;
- private String detail;
- private Date createdDate;
- private long entityId;
- private String entityName;
- public AuditLog() {
- }
- public AuditLog(String action, String detail, Date createdDate,
- long entityId, String entityName) {
- this.action = action;
- this.detail = detail;
- this.createdDate = createdDate;
- this.entityId = entityId;
- this.entityName = entityName;
- }
- @Id
- @GeneratedValue(strategy = IDENTITY)
- @Column(name = "AUDIT_LOG_ID", unique = true, nullable = false)
- public Long getAuditLogId() {
- return this.auditLogId;
- }
- .... 余下部分可以参考提供下载的源代码.
创建一个接口,所有实现了这个接口的实体类,都会写日志
- package com.mkyong.interceptor;
- //market interface
- public interface IAuditLog {
- public Long getId();
- public String getLogDeatil();
- }
这里有两个方法,getId,getLogDetail 需要实现类去实现具体的方法,也就是要被写入到日志表中的详细记录.
创建一个类实现了IAuditLog 接口,并给出接口方法的具体实现
- @Entity
- @Table(name="stock")
- public class Stock implements java.io.Serializable,IAuditLog {
- private static final long serialVersionUID = 1L;
- @Id
- @GeneratedValue(strategy=GenerationType.AUTO)
- @Column(name="STOCK_ID")
- private Integer stockId;
- @Column(name="STOCK_CODE", length=10)
- private String stockCode;
- @Column(name="STOCK_NAME", length=20)
- private String stockName;
- public Stock() {
- }
- public Stock(String stockCode, String stockName) {
- this.stockCode = stockCode;
- this.stockName = stockName;
- }
- ....省略部分getter,setter。
- @Transient
- public Long getId(){
- return this.stockId.longValue();
- }
- @Transient
- public String getLogDeatil(){
- StringBuilder sb = new StringBuilder();
- sb.append(" Stock Id : ").append(stockId)
- .append(" Stock Code : ").append(stockCode)
- .append(" Stock Name : ").append(stockName);
- return sb.toString();
- }
- }
创建记录日志的工具类,所有写日志公用
- public class AuditLogUtil{
- public static void LogIt(String action,
- IAuditLog entity){
- Session tempSession = HibernateUtil.getSessionFactory().openSession();
- try {
- tempSession.getTransaction().begin();
- AuditLog auditRecord = new AuditLog(action,entity.getLogDeatil()
- , new Date(),entity.getId(), entity.getClass().toString());
- tempSession.save(auditRecord);
- tempSession.getTransaction().commit();
- } finally {
- tempSession.close();
- }
- }
- }
创建 Hibernate interceptor 拦截器,这是重点,这里拦截所有需要记录日志的类,并处理
- public class AuditLogInterceptor extends EmptyInterceptor{
- Session session;
- private Set inserts = new HashSet();
- private Set updates = new HashSet();
- private Set deletes = new HashSet();
- public void setSession(Session session) {
- this.session=session;
- }
- @Override
- public String onPrepareStatement(String sql) {
- System.out.println("execute sql: " + sql);
- return super.onPrepareStatement(sql);
- }
- public boolean onSave(Object entity,Serializable id,
- Object[] state,String[] propertyNames,Type[] types)
- throws CallbackException {
- System.out.println("onSave");
- if (entity instanceof IAuditLog){
- inserts.add(entity);
- }
- return false;
- }
- public boolean onFlushDirty(Object entity,Serializable id,
- Object[] currentState,Object[] previousState,
- String[] propertyNames,Type[] types)
- throws CallbackException {
- System.out.println("onFlushDirty");
- if (entity instanceof IAuditLog){
- updates.add(entity);
- }
- return false;
- }
- public void onDelete(Object entity, Serializable id,
- Object[] state, String[] propertyNames,
- Type[] types) {
- System.out.println("onDelete");
- if (entity instanceof IAuditLog){
- deletes.add(entity);
- }
- }
- //called before commit into database
- public void preFlush(Iterator iterator) {
- System.out.println("preFlush");
- }
- //called after committed into database
- public void postFlush(Iterator iterator) {
- System.out.println("postFlush");
- try{
- for (Iterator it = inserts.iterator(); it.hasNext();) {
- IAuditLog entity = (IAuditLog) it.next();
- System.out.println("postFlush - insert");
- AuditLogUtil.LogIt("Saved",entity);
- }
- for (Iterator it = updates.iterator(); it.hasNext();) {
- IAuditLog entity = (IAuditLog) it.next();
- System.out.println("postFlush - update");
- AuditLogUtil.LogIt("Updated",entity);
- }
- for (Iterator it = deletes.iterator(); it.hasNext();) {
- IAuditLog entity = (IAuditLog) it.next();
- System.out.println("postFlush - delete");
- AuditLogUtil.LogIt("Deleted",entity);
- }
- } finally {
- inserts.clear();
- updates.clear();
- deletes.clear();
- }
- }
- }
这里面有几个比较常用的方法:
onSave – 保存数据的时候调用,数据还没有保存到数据库.
onFlushDirty – 更新数据时调用,但数据还没有更新到数据库
onDelete – 删除时调用.
preFlush – 保存,删除,更新 在提交之前调用 (通常在 postFlush 之前).
postFlush – 提交之后调用(commit之后)
写测试例子, 添加数据,更新数据,然后再删掉
- public class App {
- public static void main(String[] args) {
- Session session = null;
- Transaction tx = null;
- try {
- AuditLogInterceptor interceptor = new AuditLogInterceptor();
- session = HibernateUtil.getSessionFactory().withOptions().interceptor(interceptor).openSession();
- //session = HibernateUtil.getSessionFactory().openSession();
- //interceptor.setSession(session);
- //test insert
- tx = session.beginTransaction();
- Stock stockInsert = new Stock();
- stockInsert.setStockCode("1111");
- stockInsert.setStockName("yihaomen");
- session.saveOrUpdate(stockInsert);
- tx.commit();
- //test update
- tx = session.beginTransaction();
- Query query = session.createQuery("from Stock where stockCode = '1111'");
- Stock stockUpdate = (Stock)query.list().get(0);
- stockUpdate.setStockName("yihaomen-update");
- session.saveOrUpdate(stockUpdate);
- tx.commit();
- //test delete
- tx = session.beginTransaction();
- session.delete(stockUpdate);
- tx.commit();
- } catch (RuntimeException e) {
- try {
- tx.rollback();
- } catch (RuntimeException rbe) {
- // log.error("Couldn抰 roll back transaction", rbe);
- }
- throw e;
- } finally {
- if (session != null) {
- session.close();
- }
- }
- }
- }
运行结果如下:
- onSave
- execute sql: insert into stock (STOCK_CODE, STOCK_NAME) values (?, ?)
- Hibernate: insert into stock (STOCK_CODE, STOCK_NAME) values (?, ?)
- preFlush
- postFlush
- postFlush - insert
- Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)
- preFlush
- execute sql: select stock0_.STOCK_ID as STOCK_ID1_1_, stock0_.STOCK_CODE as STOCK_CO2_1_, stock0_.STOCK_NAME as STOCK_NA3_1_ from stock stock0_ where stock0_.STOCK_CODE='1111'
- Hibernate: select stock0_.STOCK_ID as STOCK_ID1_1_, stock0_.STOCK_CODE as STOCK_CO2_1_, stock0_.STOCK_NAME as STOCK_NA3_1_ from stock stock0_ where stock0_.STOCK_CODE='1111'
- preFlush
- onFlushDirty
- execute sql: update stock set STOCK_CODE=?, STOCK_NAME=? where STOCK_ID=?
- Hibernate: update stock set STOCK_CODE=?, STOCK_NAME=? where STOCK_ID=?
- postFlush
- postFlush - update
- Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)
- onDelete
- preFlush
- execute sql: delete from stock where STOCK_ID=?
- Hibernate: delete from stock where STOCK_ID=?
- postFlush
- postFlush - delete
- Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)
另外查看 auditLog 这张表, 可以看到日志成功写入
另外,如果是在SPRING 容器中使用,应该将这个interceptor 注入进去
- ..............
这样就能实现对整个项目中需要记录日志的实体类进行拦截,并记录增删改的日志记录. 还是很方便的,重点就是 Hibernate interceptor 的使用.
测试在是在 Hibernate 4.3 下测试的, 如果是hibernate 3 在openSession的时候是不同的,hibernate4用了session = HibernateUtil.getSessionFactory().withOptions().interceptor(interceptor).openSession(); 如果是Hibernate 3的话,应该是:session = HibernateUtil.getSessionFactory().openSession(interceptor);。
Hibernate4 拦截器(Interceptor) 实现实体类增删改的日志记录的更多相关文章
- struts2学习笔记--拦截器(Interceptor)和登录权限验证Demo
理解 Interceptor拦截器类似于我们学过的过滤器,是可以在action执行前后执行的代码.是我们做web开发是经常使用的技术,比如权限控制,日志.我们也可以把多个interceptor连在一起 ...
- struts2拦截器interceptor的三种配置方法
1.struts2拦截器interceptor的三种配置方法 方法1. 普通配置法 <struts> <package name="struts2" extend ...
- 过滤器(Filter)和拦截器(Interceptor)
过滤器(Filter) Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序.它依赖于servlet容器,在实现上,基于函数回调,它可以对几乎所有请求 ...
- 二十五、过滤器Filter,监听器Listener,拦截器Interceptor的区别
1.Servlet:运行在服务器上可以动态生成web页面.servlet的声明周期从被装入到web服务器内存,到服务器关闭结束.一般启动web服务器时会加载servelt的实例进行装入,然后初始化工作 ...
- Flume 拦截器(interceptor)详解
flume 拦截器(interceptor)1.flume拦截器介绍拦截器是简单的插件式组件,设置在source和channel之间.source接收到的事件event,在写入channel之前,拦截 ...
- struts2拦截器interceptor的配置方法及使用
转: struts2拦截器interceptor的配置方法及使用 (2015-11-09 10:22:28) 转载▼ 标签: it 365 分类: Struts2 NormalText Code ...
- Kafka producer拦截器(interceptor)
Producer拦截器(interceptor)是个相当新的功能,它和consumer端interceptor是在Kafka 0.10版本被引入的,主要用于实现clients端的定制化控制逻辑. 对于 ...
- Flume-NG源码阅读之SourceRunner,及选择器selector和拦截器interceptor的执行
在AbstractConfigurationProvider类中loadSources方法会将所有的source进行封装成SourceRunner放到了Map<String, SourceRun ...
- JavaWeb—拦截器Interceptor
1.概念 java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取A ...
随机推荐
- Tsinghua OJ Zuma
Description Let's play the game Zuma! There are a sequence of beads on a track at the right beginnin ...
- 【HDOJ6300】Triangle Partition(极角排序)
题意:给定3n个点,保证没有三点共线,要求找到一组点的分组方案使得它们组成的三角形之间互不相交. n<=1e3 思路:以y为第一关键字,x为第二关键字,按x递减,y递增排序 #include&l ...
- MySQL性能优化的21个最佳实践 和 mysql使用索引【转载】
今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我 们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数 ...
- mysql写入数据乱码问题的解决
mysql默认编码为latin. 我的mysql版本为5.6.安装路径下没有my.ini,但是有my-default.ini.其实mysql没有配置文件也是可以启动的,但是为了设置编码,需要将my-d ...
- Fragment实践之聊天窗体
前几天刚学了android的fragment,总是停留在简单的demo,也许永远都学不会. 今天,我要动手向我的聊天软件开刀.今天.用Fragment来实现一个例如以下图效果的聊天界面. waterm ...
- Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的使用方法总结
具体解读Jquery各Ajax函数: $.get(),$.post(),$.ajax(),$.getJSON() 一 $.get(url,[data],[callback]) 说明:url为请求地 ...
- 战五渣系列之八(绝杀AOP)
开发不用aop.程序猿的人生该会浪费多少时间.我想是时候让程序猿打败alpha狗了.程序猿解救世界. 1.概念 面向切面编程.这意味着,一切不在流水线上的东西.包含权限.日志.缓存.校验.资源.事物. ...
- iOS iOS8中 问题"registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later" 解决方式
问题重述: iOS 8中改变了通知注冊的方式,假设App须要同一时候支持iOS 7 和 8 的话,须要首先检查selector. 解决方式:在Xcode 6中 - (BOOL)application: ...
- Jenkins系列之-—02 email-ext 邮件模板
邮件通知配置 系统管理 → 系统设置 → 邮件通知 SMTP 服务器:配置 SMTP 服务器.(不填默认本地运行服务器) 用户默认邮件后缀:注册用户邮件只需填写用户名即可,后缀会加该后缀,如果填写,则 ...
- convnet源代码解析(一):基础准备
Jeremy Lin ConvNet是一个基于GPU实现的卷积神经网络开源码(C++11).是由多伦多大学的Geoffrey Hinton深度学习团队编写的,它的最初版本号是Hinton的学生Alex ...