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 ...
随机推荐
- 【ZJOI2017 Round1练习&BZOJ4766】D1T2 文艺计算姬(Prufer编码)
题意:给定一个一边点数为n,另一边点数为m,共有n*m条边的带标号完全二分图K_{n,m},求其生成树个数 mod p. 100%的数据:1 <= n,m,p <= 10^18 思路:这是 ...
- gerrit ssh 登陆设置
[root@web ~]# cat ~/.ssh/config Host gerrit User deploy-gerrit Port Hostname gerrit.demo.com Identit ...
- msp430入门编程41
msp430中C语言的软件工程--状态机建模
- linux 开机启动脚本或者服务
https://blog.csdn.net/zhuchunyan_aijia/article/details/53811368
- oracle 用户账户被锁处理
一.以管理员身份登录 SQL> conn sys/sys as sysdba; (分号是必须的但是我是以system登录的所在这不应该写conn sys/sys as sysdba应该写conn ...
- oracle coherence介绍及使用
网上除了官方用户指南,关于Coherence的介绍文章资料很少,因此总结出此文,从原理到快速指南和基本最佳实践,希望对需要的人提供一个参考. 1 Coherence 概述 1.1 Coherence是 ...
- Office EXCEL 复制粘贴 变成 #value,#REF!,#DIV怎么办
这些都是由于相对引用造成的,如下所示,我鼠标点进去之后变成了I10/L10,当数字和文字或空单元格进行加减乘除的运算就会出现这种问题 使用选择性粘贴,只粘贴数值即可.
- UICollectionView 具体解说学习
UICollectionView 和UITableView非常像,是APPLE公司在iOS 6后推出的用于处理图片这类UITableView 布局困难的控件,和UITableView 一样,它也有自己 ...
- 数据结构与算法问题 AVL二叉平衡树
AVL树是带有平衡条件的二叉查找树. 这个平衡条件必须保持,并且它必须保证树的深度是O(logN). 一棵AVL树是其每一个节点的左子树和右子树的高度最多差1的二叉查找树. (空树的高度定义为-1). ...
- 设计模式C++实现_2_简单工厂模式
简单工厂模式 主要用于创建对象. 新加入类时. 不会影响曾经的系统代码. 核心思想是用一个工厂来依据输入的条件产生不同的类,然后依据不同类的 virtual 函数得到不同的结果. 以下以苹果手机的生产 ...