http://jeffreyhsu.iteye.com/blog/191696
关键字: jpa, hibernate, 审计日志, 操作历史, 拦截器, 事件驱动, event listener
我们前段时间有个.net项目需要用j2ee改造,有个需求是要对所有的数据库操作(CRUD)都要做历史记录,要记录操作内容,操作的用户和操作时间。这样的需求称为审计日志 Audit log。
项目采用Spring构建,持久层技术采用的是 JPA规范 + Hibernate实现的方案
要实现审计日志的需求,我试用了多种技术方案,最终确定了采用Hibernate 3的新特性事件驱动架构来解决:
技术选型:
- 最土的,在所有的Dao方法中显示的编写日志记录代码
该项目以前是用.net这么干的,这种做法重复工作量太大,维护性差,并且也没实现字段级变更的记录,根本不予考虑。
- 数据库触发器 - 与数据库耦合
与数据库耦合,违背了使用hibernate的初衷,也不予考虑
- 原生的Hibernate Interceptor
优点:可以在hibernate对象操作的时候获取最为详细的运行期信息,字段名,原始值,修改后值等等。
缺点:在JPA API的封装下很难获取到hibernate的session,不能进行持久化操作。
- JPA callback / event-listener
优点:JPA规范,最为优雅简单
缺点:功能太弱,不能满足需求
- 很自然地,干这种事AOP似乎比较合适
优点:灵活,在spring容器中,可以访问所有spring bean
缺点:不能获取详细的运行期信息(字段名,原始值,等等),无法感知hibernate的事务执行,即使dao事务rollback,仍然会插入一条操作历史记录,破坏了“操作”和“历史”的一致性。
-
采用Hibernate 3的新特性 Event-listener
可以解决以上所有问题
能够取得运行期详细信息,除了能记录粗粒度的实体的保存删除操作外,还能精确追踪对实体字段修改、实体关联/级联关系的变更,能记录更新前的值、更新后的值,可以生成详细日志。
灵活解耦,跨数据库,不影响原有代码。
Hibernate3 新特性事件处理框架是hibernate 2拦截器的一个补充或者替代,由拦截器被动拦截操作事件变成事件源的主动驱动,这是一个进步。Hibernate 事件框架官方文档.
Hibernate3中定义了很多的事件,涵盖了持久化过程中不同的生命周期。简单说Session的一个方法(load, flush...)分别对应一个事件,当该方法被调用时,就会触发一个相应的事件,这个事件会被我们预先定义的事件监听器收到,再进行相应的处理。这种方式来做审计日志是再适合不过。
但也有个缺点就是这样的Event-listener是脱离主容器(比如Spring IoC环境)单独实例化的,无法访问主容器的资源(比如要取得当前登录的用户信息就会比较麻烦)。这个暂时还没解决。
在这里我们选取PostInsertEventListener(插入后事件),PostUpdateEventListener(更新后事件),PostDeleteEventListener(删除后事件)接口作为CRUD方法的监听接口。hibernate3中事件是分为pre和post,表示该发生事件前、后。这里我们全部用Post,因为PostEvent只有在数据实际改变后才会触发,假如CRUD事务因为异常回滚,则不会触发事件。
首先定义一个mark接口Historiazable,实现该接口的entity类表明是需要做审计日志的。
然后编写我们自定义的EventListener类,实现上述的事件接口。
在事件接口实现方法里在根据不同的事件编写审计日志的代码。
Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://jeffreyhsu.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" allowscriptaccess="always" quality="high" flashvars="clipboard=public%20class%20HistoryListener%20implements%20PostInsertEventListener%2C%0A%20%20%20%20%20%20%20%20PostUpdateEventListener%2C%20PostDeleteEventListener%20%7B%0A%20%20%20%0A%20%20%20%20%40Override%0A%20%20%20%20public%20void%20onPostInsert(PostInsertEvent%20event)%20%7B%0A%20%20%20%20%20%20%20%20if%20(event.getEntity()%20instanceof%20Historizable)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20%20%E4%BF%9D%E5%AD%98%20%E6%8F%92%E5%85%A5%E6%97%A5%E5%BF%97%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Override%0A%20%20%20%20public%20void%20onPostUpdate(PostUpdateEvent%20event)%20%7B%0A%20%20%20%20%20%20%20%20if%20(event.getEntity()%20instanceof%20Historizable)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20%E4%BF%9D%E5%AD%98%20%E4%BF%AE%E6%94%B9%E6%97%A5%E5%BF%97%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Override%0A%20%20%20%20public%20void%20onPostDelete(PostDeleteEvent%20event)%20%7B%0A%20%20%20%20%20%20%20%20if%20(event.getEntity()%20instanceof%20Historizable)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20%E4%BF%9D%E5%AD%98%20%E5%88%A0%E9%99%A4%E6%97%A5%E5%BF%97%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%7D"></embed>
- public class HistoryListener implements PostInsertEventListener,
- PostUpdateEventListener, PostDeleteEventListener {
-
- @Override
- public void onPostInsert(PostInsertEvent event) {
- if (event.getEntity() instanceof Historizable) {
-
- }
- }
-
- @Override
- public void onPostUpdate(PostUpdateEvent event) {
- if (event.getEntity() instanceof Historizable) {
-
- }
- }
-
- @Override
- public void onPostDelete(PostDeleteEvent event) {
- if (event.getEntity() instanceof Historizable) {
-
- }
- }
- }
配置EventListener
编辑hibernate.cfg.xml,配置监听器
Xml代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://jeffreyhsu.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" allowscriptaccess="always" quality="high" flashvars="clipboard=%20%20%20%20%3Csession-factory%3E%0A%20%20%20%20%20%20%20%20%3Clistener%20type%3D%22post-insert%22%20class%3D%22net.jeffrey.hibernate.history.HistoryListener%22%2F%3E%0A%20%20%20%20%20%20%20%20%3Clistener%20type%3D%22post-update%22%20class%3D%22net.jeffrey.hibernate.history.HistoryListener%22%2F%3E%0A%20%20%20%20%20%20%20%20%3Clistener%20type%3D%22post-delete%22%20class%3D%22net.jeffrey.hibernate.history.HistoryListener%22%2F%3E%0A%20%20%20%20%3C%2Fsession-factory%3E"></embed>
- <session-factory>
- <listener type="post-insert" class="net.jeffrey.hibernate.history.HistoryListener"/>
- <listener type="post-update" class="net.jeffrey.hibernate.history.HistoryListener"/>
- <listener type="post-delete" class="net.jeffrey.hibernate.history.HistoryListener"/>
- </session-factory>
配置持久化单元
在persistence.xml中加入
<property name="hibernate.ejb.cfgfile" value="hibernate.cfg.xml"/>
这样JPA环境启动后,就会正确装载初始化自定义的事件监听器。
源代码:
详细的代码请下载源代码,有完整的注释和单元测试用例,一看就明白了。
为了简便起见,没有建立spring或者ejb3项目,使用的是POJO方式,但都是一样的。
在源码根目录里的data.sql是为单元测试准备的基础数据,请在测试前导入到数据库中
源码是Netbeans项目,需要添加Hibernate的依赖和mysql驱动
在http://www.hibernate.org/6.html 下载最新版的Hibernate Core, Hibernate Annotations和
Hibernate EntityManager。解压缩后将lib里所有的jar添加到项目中(注意不要把ant相关jar添加进去,可能会导致netbeans无法正常构建项目)。
分享到:
相关推荐
这篇博客"JPA + Hibernate 3 CRUD操作历史审计日志的解决方案"将深入探讨如何实现这一需求。 首先,我们需要理解JPA和Hibernate的基础概念。JPA定义了一组用于持久化Java对象的API,它允许开发者通过面向对象的方式...
`JpaRepository`提供了许多基本的CRUD操作: ```java import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository, Long> { } ``` 现在,Spring会...
JPA+Spring+Hibernate的组合为Java开发者提供了一个强大的ORM解决方案,简化了数据库操作,使得开发过程更加专注于业务逻辑。通过理解并实践这个简单的配置实例,开发者能够快速掌握如何在实际项目中整合这三个组件...
标题 "Groovy+Tapestry5+Spring2.5+Hibernate3.2实现CRUD" 涉及到的是一个使用几种技术栈构建Web应用程序的示例。这个项目结合了Groovy、Tapestry 5、Spring 2.5和Hibernate 3.2,旨在展示如何在实际开发中进行数据...
总结来说,Spring JPA结合Hibernate为Java开发提供了一个强大且灵活的ORM解决方案。通过正确配置`persistence.xml`和Spring应用上下文,我们可以轻松地建立数据访问层,并使用注解驱动的方法进行数据库操作。这种...
在CRUD操作中,Hibernate通过实体类映射数据库表,简化了数据的增删改查。例如,对于用户数据的管理,可能有对应的User实体,通过Hibernate的Session接口进行操作。 **整合过程** 整合Struts2、Spring和Hibernate,...
7. **DAO(Data Access Object)接口及实现** - 用于数据库操作,通过Hibernate API进行CRUD操作。 8. **Service层接口及实现** - 定义业务逻辑,通常会注入DAO层的实现。 9. **Action类** - Struts2中的控制器,...
【标题】"spring2.5 + jpa(hibernate3) 实例源码"涉及的核心技术是Spring 2.5框架与Java Persistence API (JPA)的整合,其中JPA的具体实现是Hibernate 3。这个实例提供了如何在Spring环境中配置和使用JPA进行数据库...
**Hibernate简介** Hibernate是一个强大的Java持久化框架,它简化了数据库与Java对象之间的交互,使得...`testHiberbateJava`项目就是一个很好的学习资源,通过实际操作,你可以更深入地理解Hibernate的CRUD操作。
3. Hibernate实现JPA的依赖,如`hibernate-entitymanager`和`hibernate-core` 4. 数据库驱动,例如MySQL的`mysql-connector-java` 接下来,我们配置Spring的`dispatcher-servlet.xml`,这是Spring MVC的核心配置...
在JPA和Hibernate中,DAO通常用于执行CRUD(创建、读取、更新、删除)操作。 **DaoSupport类:** 在Spring框架中,`DaoSupport`是一个抽象类,为DAO实现提供了一些基础支持,比如初始化和关闭数据库资源。继承自`...
6. **数据库操作**:掌握SQL基本语法,以及如何通过Hibernate进行CRUD操作。 7. **整合测试**:学习如何通过JUnit、Mockito等工具对整个SSH框架进行单元测试和集成测试。 这个项目提供了一个实践平台,有助于开发者...
3. Hibernate:Hibernate是一个强大的ORM(对象关系映射)框架,它将Java对象与数据库表对应,简化了数据操作。在本项目中,Hibernate用于处理数据库交互,通过SessionFactory创建Session,进而执行CRUD操作。同时,...
2. 实现数据持久化:讲解如何使用Hibernate的注解或XML配置进行实体类和数据库表的映射,以及如何执行CRUD操作。 3. 控制器设计:讨论如何使用Struts 2的动作类来处理用户请求,以及如何利用Spring的AOP进行事务管理...
在这个例子中,Hibernate可能是用于处理数据库交互的核心工具,比如创建、读取、更新和删除(CRUD)操作。 在项目结构中: - **readme.txt** 通常包含项目的说明、安装指南或运行步骤。 - **build.xml** 是Ant或...
标题 "Spring2.5 + JPA(Hibernate)实现" 指的是在Spring框架的2.5版本中集成Java Persistence API (JPA) 和Hibernate ORM框架来管理数据库操作。这通常涉及利用Spring的IoC(Inversion of Control)和AOP(Aspect ...
3. **配置JPA**:创建persistence.xml文件,配置JPA供应商(如Hibernate)、实体类和持久化单元。 4. **实体类**:使用JPA注解(如@Entity、@Table、@Id等)定义实体类,并建立与数据库表的映射。 5. **Service和DAO...