转自: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) 实现实体类增删改的日志记录的更多相关文章

  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. To_Date函数用法

    spl> select * from emp          where dates          between          to_date('2007-06-12 10:00:0 ...

  2. mysql 获取所有的数据库名字

    mysql 获取所有的数据库名字 一.如果使用的是mysqli: $con = @mysqli_connect("localhost", "root", &qu ...

  3. HDU 5573 Binary Tree【构造】

    几天前模拟区域赛的一道题,今天发现在草稿箱里直接补个博客. 感觉这还是一道很有意思的构造题. 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5573 ...

  4. foobar2000使用cue文件播放时出现Unable to open item for playback (Object not found):的问题解决

    如下错误: 一般是找不到APE文件导致的.解决方法如下: 1.打开APE文件,对一下路径修改即可.

  5. Eclipse导入Maven项目出现:Could not calculate build plan: Plugin org.apache.maven.plugins:maven-war-plugin:2.2

    错误如下: Could not calculate build plan: Plugin org.apache.maven.plugins:maven-war-plugin:2.2 or one of ...

  6. JavaScript 中 for 循环

    在ECMAScript5(简称 ES5)中,有三种 for 循环,分别是: 简单for循环 for-in forEach 在2015年6月份发布的ECMAScript6(简称 ES6)中,新增了一种循 ...

  7. ArcGIS10.x Engine直连提示连接超时ORA-12170 来自:http://www.iarcgis.com/?p=1004

    导语 随着Esri大力宣传直连,用户也越来越由服务连接,改为直连,当然ArcGIS Engine开发用户也不例外. 环境 Oracle数据库,ArcGIS版本不限,不过由于9版本多以服务连接,以10版 ...

  8. 虚拟社会(Virtual Society)

    虚拟社会(Virtual Society),又称赛博社会(Cyber Society),是指不同网民之间经由计算机.远程通讯终端等技术设备相互连接起来以进行信息的共享.互动与交流,并在其中进行社会交往 ...

  9. 元数据的概念以及相关的操作os模块、shutil模块

    查看文件的元数据 stat [OPTION]… FILE… OPTION: -f 输出文件系统的状态,而非文件的状态 -t 显示简要格式的文件元数据信息 FILE:可同时查看多个文件的元数据信息,多个 ...

  10. python字符串连接方法效率比较

    方法1:直接通过加号(+)操作符连接 1 website = 'python' + 'tab' + '.com' 方法2:join方法 1 2 listStr = ['python', 'tab',  ...