- 浏览: 60064 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
mlshenlong:
您好!我的配置如下:X ...
hibernate3新特性EventListener完整实例 -
u012897256:
有没有什么方法,可以在依然使用update的情况下,可以得到修 ...
hibernate3新特性EventListener完整实例 -
u012897256:
但是,我把update改为merge方法后,就算修改的操作不成 ...
hibernate3新特性EventListener完整实例 -
guijiaohu:
能否提供一下其他相关类,我得qq是895225064,多谢啊。 ...
hibernate3新特性EventListener完整实例 -
lzh166:
SessionAgentTool 和 BaseEntity 类 ...
hibernate3新特性EventListener完整实例
参考文章:http://www.iteye.com/topic/477134
项目中要对数据的更新做审计,比如订单的每一项的变化,会员主要信息的变化等。
老版的程序是这样处理的,在更新前先查一次数据库,然后依次比对各列,得到修改变化的信息。但在我们新版程序中,由于用到了singleSesssion,那么在一个Session中不允许出现两个ID相同的对象,所以老路是走不通的。
随后到网上搜索,就找到了上面这篇文章,博主写得很好,按照这个思路我也把例子写出来了。但是后面还碰到了几个问题,在这里也分享一下,希望能对大家有帮助。
现有主要配置如下:
<bean id="crudListener" class="com.liut.core.listener.CrudListener"> </bean>
按照例子程序可以输出log日志,说明监听器是起作用的。那么接下来要把日志信息写入数据库,在监听器中引入IAuditLogService审计日志服务接口,用来保存日志信息。新的配置如下:
<bean id="crudListener" class="com.liut.core.listener.CrudListener"> <property name="auditLogService"><ref bean="auditLogService" /></property> </bean>
启动应用控制台报错,是因为这违反了spring的原则,auditLogService引用了sessionFactory,现在sessionFactory的监听器里要引用auditLogService,当然是不允许的。好吧换一种方式
/** * 初始化审计日志服务类 * @Title: init void * @throws */ private synchronized void init(){ if(auditLogService == null){ WebApplicationContext context =WebApplicationContextUtils.getWebApplicationContext(SessionAgentTool.getSession().getRealHttpSession().getServletContext()); auditLogService =(IAuditLogService)context.getBean("auditLogService"); } }
(其中SessionAgentTool是工具类,可以得到当前session)
结果执行修改动作时,一点反应没有,没有记录日志,也没有错误。控制台的日志信息照常输出。这个汗。。。啊!经过数小时的修改、测试,终未果。静下心来分析整个过程,终于发现问题了,在PostUpdateEventListener中,调用保存日志的方法,这时PostUpdateEventListener还会被触发,这不成死循环了?
问题找到就好办了,现在重写保存的方法,用SQL绕过hibernate监听。OK,测试通过!
到这里还没有结束,刚才一直用码表来测试,现在来测试订单,MYGOD!Hibernate在执行flush时数据下标越界【Caused by: java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
】,这怎么可能?后来发现了规律,只在在事务中间的对象记录日志就会有这个错误。难道是事务的问题???看看现在是这样配的:
<bean id="transInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor" > <property name="transactionManager"> <ref bean="transactionManager"/> </property> <property name="transactionAttributes"> <props> <prop key="query*">PROPAGATION_SUPPORTS,readOnly</prop> <prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop> <prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop> <prop key="select*">PROPAGATION_SUPPORTS,readOnly</prop> <prop key="load*">PROPAGATION_SUPPORTS,readOnly</prop> <prop key="stat*">PROPAGATION_SUPPORTS,readOnly</prop> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
隐约记得有个子事务的说法也没有用过,搜索一下,修改如下
<bean id="transInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor" >
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="query*">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="find*">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="get*">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="select*">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="load*">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="stat*">PROPAGATION_SUPPORTS,readOnly</prop>
<prop key="log">PROPAGATION_REQUIRES_NEW</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
注意红色部分是添加项,也就是SQL保存日志的方法名,让它启用子事务。重启测试,OK,万事大吉了!
后面针对需求做了简单配置,因为不是所有的表都需要审计,而且数据量也会超大的,那么可以做到想记哪个表就记哪个表。下面是主要代码片
监听器配置文件
<!-- 审计日志配置策略: 1.可用的关键字有:insertAllow,insertDeny,updateAllow,updateDeny,deleteAllow,deleteDeny 2.没有配置对象的策略,所有字段不记录 3.allow和deny都配置的按allow验证,并忽略deny 4.allow和deny都允许指定all关键字 5.多个字段用英文逗号隔开 --> <bean id="crudListener" class="com.liut.core.listener.CrudListener"> <property name="auditableEntitys"> <map> <entry key="Order"> <map> <entry key="insertAllow"> <value>ordNo</value> </entry> <entry key="updateAllow"> <value>all</value> </entry> <entry key="deleteAllow"> <value>ordNo</value> </entry> </map> </entry> <entry key="OrderDetail"> <map> <entry key="insertDeny"> <value>all</value> </entry> <entry key="updateAllow"> <value>all</value> </entry> <entry key="deleteDeny"> <value>all</value> </entry> </map> </entry> <entry key="User"> <map> <entry key="insertDeny"> <value>userName</value> </entry> <entry key="updateAllow"> <value>all</value> </entry> <entry key="deleteDeny"> <value>userName</value> </entry> </map> </entry> <entry key="UserInfo"> <map> <entry key="insertAllow"> <value>email</value> </entry> <entry key="updateAllow"> <value>all</value> </entry> <entry key="deleteAllow"> <value>email</value> </entry> </map> </entry> </map> </property> </bean>
sessionFactory配置文件
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"><ref bean="datasource"/></property> 。。。(省略部分配置) <property name="eventListeners"> <map> <entry key="post-insert"> <ref local="crudListener"/> </entry> <entry key="post-update"> <ref local="crudListener"/> </entry> <entry key="post-delete"> <ref local="crudListener"/> </entry> </map> </property> </bean>
CrudListener.java
package com.liut.core.listener; 。。。(省略import信息) /** * Hibernate增删改监听器,记录审记日志 * <p>类名称:EntityCrudListener</p> * <p>类描述:post方法在数据更新后执行,pre方法在数据更新前执行 </p> * <p>创建人:LiuTong</p> * <p>创建时间:Sep 26, 2012 2:39:52 PM </p> * <p>修改人:LiuTong</p> * <p>修改时间:Sep 26, 2012 2:39:52 PM </p> * <p>修改备注: </p> * @version */ public class CrudListener implements PostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener { private static final long serialVersionUID = 1L; private static final String INSERT = "INSERT"; private static final String UPDATE = "UPDATE"; private static final String DELETE = "DELETE"; /** * 允许或不允许全部时,指定all即可 */ public static final String ALL = "all"; private static final Log logger = LogFactory.getLog(CrudListener.class); /** * 配置审计对象的记录策略 */ private Map<String,Map<String,String>> auditableEntitys; /** * 审计日志服务类 */ private IAuditLogService auditLogService; @Override public void onPostInsert(PostInsertEvent event) { if (auditableEntitys.containsKey(event.getEntity().getClass().getSimpleName()) && event.getEntity() instanceof BaseEntity) { init(); // 保存 插入日志 AuditLog log = newAuditLog(); log.setTableName(event.getEntity().getClass().getSimpleName()); log.setDataId(((BaseEntity)event.getEntity()).getId().toString()); log.setDoType(INSERT); { Object[] state = event.getState(); String[] fields = event.getPersister().getPropertyNames(); String content = ""; if(state != null && fields != null && state.length == fields.length){ for(int i = 0 ; i < fields.length ; i ++){ if(isLog(event.getEntity(),fields[i],INSERT)){ content = addStr(null, state, fields, content, i); } } } log.setContent("[" + content + "]"); } logger.debug("插入审计日志 INSERT AuditLog "); insert(log); } } @Override public void onPostUpdate(PostUpdateEvent event) { if (auditableEntitys.containsKey(event.getEntity().getClass().getSimpleName()) && event.getEntity() instanceof BaseEntity) { init(); // 保存 修改日志 AuditLog log = newAuditLog(); log.setTableName(event.getEntity().getClass().getSimpleName()); log.setDataId(((BaseEntity)event.getEntity()).getId().toString()); log.setDoType(UPDATE); { Object[] oldState = event.getOldState(); Object[] newState = event.getState(); String[] fields = event.getPersister().getPropertyNames(); String content = ""; if(oldState != null && newState != null && fields != null && oldState.length == newState.length && oldState.length == fields.length){ for(int i = 0 ; i < fields.length ; i ++){ if(isLog(event.getEntity(),fields[i],UPDATE)){ if((newState[i] == null && oldState[i] != null) || (newState[i] != null && !newState[i].equals(oldState[i]) )){ content = addStr(oldState, newState, fields, content, i); } } } } log.setContent("[" + content + "]"); } logger.debug("插入审计日志 UPDATE AuditLog "); insert(log); } } @Override public void onPostDelete(PostDeleteEvent event) { if (auditableEntitys.containsKey(event.getEntity().getClass().getSimpleName()) && event.getEntity() instanceof BaseEntity) { init(); // 保存 删除日志 AuditLog log = newAuditLog(); log.setTableName(event.getEntity().getClass().getSimpleName()); log.setDataId(((BaseEntity)event.getEntity()).getId().toString()); log.setDoType(DELETE); { Object[] state = event.getDeletedState(); String[] fields = event.getPersister().getPropertyNames(); String content = ""; if(state != null && fields != null && state.length == fields.length){ for(int i = 0 ; i < fields.length ; i ++){ if(isLog(event.getEntity(),fields[i],DELETE)){ content = addStr(null, state, fields, content, i); } } } log.setContent("[" + content + "]"); } logger.debug("插入审计日志 DELETE AuditLog "); insert(log); } } /** * 记录审计日志 * @Title: insert * @param log void * @throws */ private void insert(AuditLog log) { auditLogService.log(log); } /** * 创建日志对象,同时设置操作人操作时间等信息 * @Title: newAuditLog * @return AuditLog * @throws */ private AuditLog newAuditLog() { Visit visit = SessionAgentTool.getCurrentVisit(); AuditLog log = new AuditLog(); log.setDoTime(TimeUtils.getCurrentStandardTime()); log.setEditorId(visit.getId()); log.setEditorName(visit.getUserOptionName()); return log; } /** * 验证策略是否允许记录日志,规则如下: * <ol> * <li>可用的关键字有:insertAllow,insertDeny,updateAllow,updateDeny,deleteAllow,deleteDeny</li> * <li>没有配置对象的策略,所有字段不记录</li> * <li>allow和deny都配置的按allow验证,并忽略deny</li> * <li>allow和deny都允许指定all关键字</li> * <li>多个字段用英文逗号隔开</li> * </ol> * @Title: isLog * @param entity * @param string * @param string2 * @return boolean * @throws */ private boolean isLog(Object entity, String field, String op) { Map<String,String> entityConfig = auditableEntitys.get(entity.getClass().getSimpleName()); if(entityConfig != null){ String allowFields = entityConfig.get(op.toLowerCase() + "Allow"); if(allowFields != null){ if(allowFields.equals(ALL) || containsField(allowFields,field)){ //配置ALL,所有允许 return true; } }else{ String denyFields = entityConfig.get(op.toLowerCase() + "Deny"); if(denyFields != null){ if(denyFields.equals(ALL) || containsField(denyFields,field)){ //配置ALL,所有不允许 return false; } } return true; } }else{ } //缺省不记录 return false; } /** * 配置中是否包含当前字段 * @Title: containsField * @param fields * @param field * @return boolean * @throws */ private boolean containsField(String fields, String field) { String[] fs = fields.split(","); for(String f : fs){ if(f.equals(field)){ return true; } } return false; } /** * 向content追加一个修改项 * @Title: addStr * @param oldState * @param newState * @param fields * @param content * @param i * @return String * @throws */ private String addStr(Object[] oldState, Object[] newState, String[] fields, String content, int i) { if(content.length() < 1000){ if(content.length() > 0){ content += ","; } content += "{columnName:\"" + fields[i] + "\",oldValue:\"" + (oldState == null ? "" : String.valueOf(oldState[i])) + "\",newValue:\"" + String.valueOf(newState[i]) + "\"}"; }else{ logger.warn("审计长度超过1000"); } return content; } /** * 初始化审计日志服务类 * @Title: init void * @throws */ private synchronized void init(){ if(auditLogService == null){ WebApplicationContext context =WebApplicationContextUtils.getWebApplicationContext(SessionAgentTool.getSession().getRealHttpSession().getServletContext()); auditLogService =(IAuditLogService)context.getBean("auditLogService"); } } public void setAuditableEntitys( Map<String, Map<String, String>> auditableEntitys) { this.auditableEntitys = auditableEntitys; } }
评论
XML文件:
<bean id="historyListener" class="net.shopxx.listener.HistoryListener"></bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="eventListeners">
<map>
<entry key="post-insert">
<ref bean="historyListener" />
</entry>
<entry key="post-update">
<ref bean="historyListener" />
</entry>
<entry key="post-delete">
<ref bean="historyListener" />
</entry>
</map>
</property>
</bean>
监听类:
public class HistoryListener extends DefaultLoadEventListener implements PostInsertEventListener,
PostUpdateEventListener, PostDeleteEventListener {
private static final long serialVersionUID = -3142415669713724726L;
@Override
public void onPostDelete(PostDeleteEvent arg0) {
Object object=arg0.getEntity();
System.out.println(object);
}
@Override
@PostUpdate
public void onPostUpdate(PostUpdateEvent event) {
System.out.println(event.getEntity().getClass().getSimpleName()+":更新完毕");
for (int i = 0; i < event.getState().length; i++) {
// 更新前的值
Object oldValue = event.getOldState()[i];
// 更新后的新值
Object newValue = event.getState()[i];
//更新的属性名
String propertyName = event.getPersister().getPropertyNames()[i];
}
}
@Override
public void onPostInsert(PostInsertEvent arg0) {
Object object=arg0.getEntity();
System.out.println(object);
}
}
但是执行update的时候,老是不执行onPostUpdate方法,请问下问题出在哪里?谢谢!
同时也测试了onPreUpdate方法,单表更新时正常,但一个事务中有多个表时,用upadte更新仍然得不到。
相关推荐
10. **EventListener和Interceptor**:允许自定义事件监听器和拦截器,实现特定的业务逻辑,如在数据持久化前后进行操作。 11. **JPA(Java Persistence API)集成**:Hibernate也可作为JPA的实现,提供标准的持久...
Hibernate允许开发者通过实现特定接口(如Interceptor或EventListener)进行定制化操作,如在对象持久化前后执行自定义逻辑。 11. **HQL与JPQL** Hibernate Query Language(HQL)和Java Persistence Query ...
7. **Event and Listener**:Hibernate的事件监听机制允许我们在特定操作(如save、update、delete等)前后执行自定义逻辑。通过实现Listener接口并注册,我们可以定制化Hibernate的行为。 8. **Type System**:...
11. **事件监听器**:Hibernate支持在特定操作(如保存、更新、删除等)前后触发自定义逻辑,通过实现EventListener接口,开发者可以扩展Hibernate的行为。 12. **类型系统**:Hibernate提供了丰富的类型系统,不仅...
Hibernate具有良好的扩展性,如插件化的缓存实现(如Ehcache、Infinispan)、拦截器(Interceptor)和事件监听器(EventListener),使得开发者可以根据需求定制和扩展其功能。 10. 最佳实践: 在实际应用中,...
10. Hibernate事件和监听器:允许开发者在特定操作(如对象加载、保存、删除等)前后执行自定义逻辑,通过实现和注册EventListener子类。 11. Second Level Cache:Hibernate提供了二级缓存机制,可以提高数据访问...
另外,书中还会涉及 Hibenate 的扩展,如使用拦截器(Interceptor)进行自定义行为,或者利用事件监听器(EventListener)在特定操作前后执行额外逻辑。此外,Spring框架与Hibernate的整合也是常见的应用场景,这...
3. **持久化类(Persistent Class)**:Hibernate能够管理的类称为持久化类,它们的对象被称为持久化对象。这些对象可以在数据库中创建、读取、更新和删除,通过Session接口进行操作。 4. **SessionFactory和...
- **事件监听器(Event Listener)**: 类似于拦截器,但更灵活,可以监听更多的事件类型。 - **自定义类型(Custom Types)**: 可以定义自己的数据类型,例如日期格式化等。 ### 总结 通过以上内容的介绍,我们了解到...
3. SessionFactory接口:是Hibernate的核心工厂类,用于创建Session实例。一个SessionFactory代表了一个数据库连接池,它的生命周期通常与应用程序相同,创建后就不再改变。 4. Entity类和映射文件:Entity类是Java...
还包括CGLIB和JPA支持、拦截器(Interceptor)、事件监听器(Event Listener)等,这些都为开发者提供了更大的灵活性和定制能力。 通过学习并熟练掌握上述内容,开发者将能够充分利用Hibernate的强大功能,高效地...
对于使用 Hibernate 进行持久化管理的项目,Snaker-Hibernate 提供了与 Hibernate 的整合,使得流程实例、任务实例等对象可以无缝存取于 Hibernate 管理的数据库中。这种集成简化了数据库操作,让开发者可以专注于...
- **Observer模式**:如Swing中的事件监听,以及`java.util.EventListener`接口。 单例模式的特点包括: - 只能有一个实例存在。 - 单例类必须自己创建并管理自己的唯一实例。 - 提供一个全局访问点来获取这个实例...
此外,Activiti支持自定义行为(如EventListener、TaskListener)和扩展点,以满足定制化需求。 总结来说,Activiti工作流是一个功能强大且灵活的流程管理工具,通过深入学习和实践本操作手册,Java开发者能够熟练...
深入到源码层面,jBPM的工作流引擎实现了一系列核心概念,如流程定义(Process Definition)、流程实例(Process Instance)、任务(Task)和事件(Event)。开发者可能需要理解这些实体间的交互机制,例如,当一个...
3. **数据访问**: 支持 JDBC、Hibernate、JPA 等多种数据访问技术。 4. **测试**: 提供了强大的支持工具,便于单元测试和集成测试。 5. **微服务架构**: 可以用于构建微服务架构中的服务发现、配置管理等功能。 ###...
public class EventListener { @EventHandler public void handleEvent(Event event) { // 事件处理逻辑 } } ``` #### 九、OSGi与Spring框架的整合 OSGi可以与Spring框架整合,利用Spring的强大功能进行依赖...
在Java中,这通常通过实现EventListener接口和对应的事件处理方法(如actionPerformed())来实现。 3. **MySQL数据库**:MySQL是一个流行的开源关系型数据库管理系统(RDBMS),适合于中小型应用程序的数据存储。在...
Java的EventObject、EventListener和EventDispatcher等接口可以构建这种模型。 7. **数据持久化**:为了保存用户信息和聊天记录,通常会涉及数据库操作。开发者需要了解如何使用Java的JDBC API与数据库交互,或者...