转自:https://blog.csdn.net/he90227/article/details/44783099

开发应用程序的过程中,经常会对一些比较重要的数据修改都需要写日志。在实际工作的工程中,这些数据都是存在表中的, 一个常见的做法是用触发器,在增删改的时候,用触发器将数据写入到另一张表中去,但个人不推荐这么做,原因如下:
1. 如果有多个表,得写很多触发器。
2. 触发器与数据库特性关联太紧,不同的数据库,虽然思路一样,但语法却不太一样。
对数据库表操作的日志记录,完全可以利用Hibernate的Interceptor特性来实现,也就是拦截器。下面用一个具体的例子来说明如何使用Hibernate的Interceptor。

创建一个表,用来记录日志的表

  1. Create TABLE  `auditlog` (
  2.   `AUDIT_LOG_ID` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  3.   `ACTION` VARCHAR(100) NOT NULL,
  4.   `DETAIL` text NOT NULL,
  5.   `CreateD_DATE` DATE NOT NULL,
  6.   `ENTITY_ID` BIGINT(20) UNSIGNED NOT NULL,
  7.   `ENTITY_NAME` VARCHAR(255) NOT NULL,
  8.   PRIMARY KEY (`AUDIT_LOG_ID`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

创建这个表对应的实体类:

  1. @Entity
  2. @Table(name = "auditlog")
  3. public class AuditLog implements java.io.Serializable {
  4.     private Long auditLogId;
  5.     private String action;
  6.     private String detail;
  7.     private Date createdDate;
  8.     private long entityId;
  9.     private String entityName;
  10.     public AuditLog() {
  11.     }
  12.     public AuditLog(String action, String detail, Date createdDate,
  13.             long entityId, String entityName) {
  14.         this.action = action;
  15.         this.detail = detail;
  16.         this.createdDate = createdDate;
  17.         this.entityId = entityId;
  18.         this.entityName = entityName;
  19.     }
  20.     @Id
  21.     @GeneratedValue(strategy = IDENTITY)
  22.     @Column(name = "AUDIT_LOG_ID", unique = true, nullable = false)
  23.     public Long getAuditLogId() {
  24.         return this.auditLogId;
  25.     }
  26.         .... 余下部分可以参考提供下载的源代码.

创建一个接口,所有实现了这个接口的实体类,都会写日志

  1. package com.mkyong.interceptor;
  2. //market interface
  3. public interface IAuditLog {    
  4.     public Long getId();    
  5.     public String getLogDeatil();
  6. }

这里有两个方法,getId,getLogDetail 需要实现类去实现具体的方法,也就是要被写入到日志表中的详细记录.

创建一个类实现了IAuditLog 接口,并给出接口方法的具体实现

  1. @Entity
  2. @Table(name="stock")
  3. public class Stock implements java.io.Serializable,IAuditLog  {
  4.     private static final long serialVersionUID = 1L;
  5.     @Id
  6.     @GeneratedValue(strategy=GenerationType.AUTO)
  7.     @Column(name="STOCK_ID")
  8.     private Integer stockId;
  9.     
  10.     @Column(name="STOCK_CODE", length=10)
  11.     private String stockCode;
  12.     
  13.     @Column(name="STOCK_NAME", length=20)
  14.     private String stockName;
  15.     public Stock() {
  16.     }
  17.     public Stock(String stockCode, String stockName) {
  18.         this.stockCode = stockCode;
  19.         this.stockName = stockName;
  20.     }
  21.     ....省略部分getter,setter
  22.     @Transient
  23.     public Long getId(){
  24.         return this.stockId.longValue();
  25.     }
  26.     
  27.     @Transient
  28.     public String getLogDeatil(){
  29.         StringBuilder sb = new StringBuilder();
  30.         sb.append(" Stock Id : ").append(stockId)
  31.         .append(" Stock Code : ").append(stockCode)
  32.         .append(" Stock Name : ").append(stockName);
  33.         return sb.toString();
  34.     }
  35. }

创建记录日志的工具类,所有写日志公用

  1. public class AuditLogUtil{
  2.     
  3.     public static void LogIt(String action,
  4.         IAuditLog entity){
  5.         
  6.         Session tempSession = HibernateUtil.getSessionFactory().openSession();
  7.             
  8.         try {
  9.             tempSession.getTransaction().begin();
  10.             AuditLog auditRecord = new AuditLog(action,entity.getLogDeatil()
  11.                     , new Date(),entity.getId(), entity.getClass().toString());
  12.             tempSession.save(auditRecord);
  13.             tempSession.getTransaction().commit();
  14.             
  15.         } finally {    
  16.             tempSession.close();
  17.             
  18.         }
  19.             
  20.     }
  21. }

创建 Hibernate interceptor 拦截器,这是重点,这里拦截所有需要记录日志的类,并处理

  1. public class AuditLogInterceptor extends EmptyInterceptor{
  2.     
  3.     Session session;
  4.     private Set inserts = new HashSet();
  5.     private Set updates = new HashSet();
  6.     private Set deletes = new HashSet();
  7.     
  8.     public void setSession(Session session) {
  9.         this.session=session;
  10.     }
  11.         
  12.     @Override
  13.     public String onPrepareStatement(String sql) {
  14.         System.out.println("execute sql: " + sql);
  15.         return super.onPrepareStatement(sql);
  16.     }
  17.     public boolean onSave(Object entity,Serializable id,
  18.         Object[] state,String[] propertyNames,Type[] types)
  19.         throws CallbackException {
  20.         
  21.         System.out.println("onSave");
  22.         
  23.         if (entity instanceof IAuditLog){
  24.             inserts.add(entity);
  25.         }
  26.         return false;
  27.             
  28.     }
  29.     
  30.     public boolean onFlushDirty(Object entity,Serializable id,
  31.         Object[] currentState,Object[] previousState,
  32.         String[] propertyNames,Type[] types)
  33.         throws CallbackException {
  34.     
  35.         System.out.println("onFlushDirty");
  36.         
  37.         if (entity instanceof IAuditLog){
  38.             updates.add(entity);
  39.         }
  40.         return false;
  41.         
  42.     }
  43.     
  44.     public void onDelete(Object entity, Serializable id,
  45.         Object[] state, String[] propertyNames,
  46.         Type[] types) {
  47.         
  48.         System.out.println("onDelete");
  49.         
  50.         if (entity instanceof IAuditLog){
  51.             deletes.add(entity);
  52.         }
  53.     }
  54.     //called before commit into database
  55.     public void preFlush(Iterator iterator) {
  56.         System.out.println("preFlush");
  57.     }    
  58.     
  59.     //called after committed into database
  60.     public void postFlush(Iterator iterator) {
  61.         System.out.println("postFlush");
  62.         
  63.         try{
  64.         
  65.             for (Iterator it = inserts.iterator(); it.hasNext();) {
  66.                 IAuditLog entity = (IAuditLog) it.next();
  67.                 System.out.println("postFlush - insert");
  68.                 
  69.                 AuditLogUtil.LogIt("Saved",entity);
  70.             }    
  71.             
  72.             for (Iterator it = updates.iterator(); it.hasNext();) {
  73.                 IAuditLog entity = (IAuditLog) it.next();
  74.                 System.out.println("postFlush - update");
  75.                 AuditLogUtil.LogIt("Updated",entity);
  76.             }    
  77.             
  78.             for (Iterator it = deletes.iterator(); it.hasNext();) {
  79.                 IAuditLog entity = (IAuditLog) it.next();
  80.                 System.out.println("postFlush - delete");
  81.                 AuditLogUtil.LogIt("Deleted",entity);
  82.             }    
  83.             
  84.         } finally {
  85.             inserts.clear();
  86.             updates.clear();
  87.             deletes.clear();
  88.         }
  89.     }    
  90.     
  91. }

这里面有几个比较常用的方法:
onSave – 保存数据的时候调用,数据还没有保存到数据库.
onFlushDirty – 更新数据时调用,但数据还没有更新到数据库
onDelete – 删除时调用.
preFlush – 保存,删除,更新 在提交之前调用 (通常在 postFlush 之前).
postFlush – 提交之后调用(commit之后)

写测试例子, 添加数据,更新数据,然后再删掉

  1. public class App {
  2.     public static void main(String[] args) {
  3.         Session session = null;
  4.         Transaction tx = null;
  5.         try {
  6.             AuditLogInterceptor interceptor = new AuditLogInterceptor();
  7.             
  8.             session = HibernateUtil.getSessionFactory().withOptions().interceptor(interceptor).openSession();
  9.             //session = HibernateUtil.getSessionFactory().openSession();
  10.             //interceptor.setSession(session);
  11.             
  12.             //test insert
  13.             tx = session.beginTransaction();
  14.             Stock stockInsert = new Stock();
  15.             stockInsert.setStockCode("1111");
  16.             stockInsert.setStockName("yihaomen");
  17.             session.saveOrUpdate(stockInsert);
  18.             tx.commit();
  19.             
  20.             //test update
  21.             tx = session.beginTransaction();
  22.             Query query = session.createQuery("from Stock where stockCode = '1111'");
  23.             Stock stockUpdate = (Stock)query.list().get(0);
  24.             stockUpdate.setStockName("yihaomen-update");
  25.             session.saveOrUpdate(stockUpdate);
  26.             tx.commit();
  27.             
  28.             //test delete
  29.             tx = session.beginTransaction();
  30.             session.delete(stockUpdate);
  31.             tx.commit();
  32.         } catch (RuntimeException e) {
  33.             try {
  34.                 tx.rollback();
  35.             } catch (RuntimeException rbe) {
  36.                 // log.error("Couldn抰 roll back transaction", rbe);
  37.             }
  38.             throw e;
  39.         } finally {
  40.             if (session != null) {
  41.                 session.close();
  42.             }
  43.         }
  44.     }
  45. }

运行结果如下:

  1. onSave
  2. execute sql: insert into stock (STOCK_CODE, STOCK_NAME) values (?, ?)
  3. Hibernate: insert into stock (STOCK_CODE, STOCK_NAME) values (?, ?)
  4. preFlush
  5. postFlush
  6. postFlush - insert
  7. Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)
  8. preFlush
  9. 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'
  10. 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'
  11. preFlush
  12. onFlushDirty
  13. execute sql: update stock set STOCK_CODE=?, STOCK_NAME=? where STOCK_ID=?
  14. Hibernate: update stock set STOCK_CODE=?, STOCK_NAME=? where STOCK_ID=?
  15. postFlush
  16. postFlush - update
  17. Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)
  18. onDelete
  19. preFlush
  20. execute sql: delete from stock where STOCK_ID=?
  21. Hibernate: delete from stock where STOCK_ID=?
  22. postFlush
  23. postFlush - delete
  24. Hibernate: insert into auditlog (ACTION, CreateD_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) values (?, ?, ?, ?, ?)

另外查看 auditLog 这张表, 可以看到日志成功写入

另外,如果是在SPRING 容器中使用,应该将这个interceptor 注入进去

  1.   
  2.       
  3.       
  4.           
  5.               
  6.           
  7.         
  8.         ..............
  9.   

这样就能实现对整个项目中需要记录日志的实体类进行拦截,并记录增删改的日志记录. 还是很方便的,重点就是 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) 实现实体类增删改的日志记录的更多相关文章

  1. struts2学习笔记--拦截器(Interceptor)和登录权限验证Demo

    理解 Interceptor拦截器类似于我们学过的过滤器,是可以在action执行前后执行的代码.是我们做web开发是经常使用的技术,比如权限控制,日志.我们也可以把多个interceptor连在一起 ...

  2. struts2拦截器interceptor的三种配置方法

    1.struts2拦截器interceptor的三种配置方法 方法1. 普通配置法 <struts> <package name="struts2" extend ...

  3. 过滤器(Filter)和拦截器(Interceptor)

    过滤器(Filter) Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序.它依赖于servlet容器,在实现上,基于函数回调,它可以对几乎所有请求 ...

  4. 二十五、过滤器Filter,监听器Listener,拦截器Interceptor的区别

    1.Servlet:运行在服务器上可以动态生成web页面.servlet的声明周期从被装入到web服务器内存,到服务器关闭结束.一般启动web服务器时会加载servelt的实例进行装入,然后初始化工作 ...

  5. Flume 拦截器(interceptor)详解

    flume 拦截器(interceptor)1.flume拦截器介绍拦截器是简单的插件式组件,设置在source和channel之间.source接收到的事件event,在写入channel之前,拦截 ...

  6. struts2拦截器interceptor的配置方法及使用

    转: struts2拦截器interceptor的配置方法及使用 (2015-11-09 10:22:28) 转载▼ 标签: it 365 分类: Struts2  NormalText Code  ...

  7. Kafka producer拦截器(interceptor)

    Producer拦截器(interceptor)是个相当新的功能,它和consumer端interceptor是在Kafka 0.10版本被引入的,主要用于实现clients端的定制化控制逻辑. 对于 ...

  8. Flume-NG源码阅读之SourceRunner,及选择器selector和拦截器interceptor的执行

    在AbstractConfigurationProvider类中loadSources方法会将所有的source进行封装成SourceRunner放到了Map<String, SourceRun ...

  9. JavaWeb—拦截器Interceptor

    1.概念 java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了一种可以提取A ...

随机推荐

  1. Tsinghua OJ Zuma

    Description Let's play the game Zuma! There are a sequence of beads on a track at the right beginnin ...

  2. 【HDOJ6300】Triangle Partition(极角排序)

    题意:给定3n个点,保证没有三点共线,要求找到一组点的分组方案使得它们组成的三角形之间互不相交. n<=1e3 思路:以y为第一关键字,x为第二关键字,按x递减,y递增排序 #include&l ...

  3. MySQL性能优化的21个最佳实践 和 mysql使用索引【转载】

    今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我 们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数 ...

  4. mysql写入数据乱码问题的解决

    mysql默认编码为latin. 我的mysql版本为5.6.安装路径下没有my.ini,但是有my-default.ini.其实mysql没有配置文件也是可以启动的,但是为了设置编码,需要将my-d ...

  5. Fragment实践之聊天窗体

    前几天刚学了android的fragment,总是停留在简单的demo,也许永远都学不会. 今天,我要动手向我的聊天软件开刀.今天.用Fragment来实现一个例如以下图效果的聊天界面. waterm ...

  6. Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的使用方法总结

    具体解读Jquery各Ajax函数: $.get(),$.post(),$.ajax(),$.getJSON() 一 $.get(url,[data],[callback]) 说明:url为请求地 ...

  7. 战五渣系列之八(绝杀AOP)

    开发不用aop.程序猿的人生该会浪费多少时间.我想是时候让程序猿打败alpha狗了.程序猿解救世界. 1.概念 面向切面编程.这意味着,一切不在流水线上的东西.包含权限.日志.缓存.校验.资源.事物. ...

  8. iOS iOS8中 问题&quot;registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later&quot; 解决方式

    问题重述: iOS 8中改变了通知注冊的方式,假设App须要同一时候支持iOS 7 和 8 的话,须要首先检查selector. 解决方式:在Xcode 6中 - (BOOL)application: ...

  9. Jenkins系列之-—02 email-ext 邮件模板

    邮件通知配置 系统管理 → 系统设置 → 邮件通知 SMTP 服务器:配置 SMTP 服务器.(不填默认本地运行服务器) 用户默认邮件后缀:注册用户邮件只需填写用户名即可,后缀会加该后缀,如果填写,则 ...

  10. convnet源代码解析(一):基础准备

    Jeremy Lin ConvNet是一个基于GPU实现的卷积神经网络开源码(C++11).是由多伦多大学的Geoffrey Hinton深度学习团队编写的,它的最初版本号是Hinton的学生Alex ...