zoukankan      html  css  js  c++  java
  • 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);。

  • 相关阅读:
    grid 布局
    数组对象
    定义换页时表现
    判断一个对象是否为空
    内存管理 内存泄漏
    arguments对象
    String类型
    对象 实例
    iOS 之 UIWebView
    iOS 之 内嵌网页
  • 原文地址:https://www.cnblogs.com/sharpest/p/10020849.html
Copyright © 2011-2022 走看看