hibernate使用delete(Entity)方法的顺序

探讨了在Hibernate中使用实体删除方法可能导致的事务内SQL执行顺序问题,并提供了一种通过执行自定义SQL来确保正确执行顺序的解决方案。

hibernate为我们提供了删除直接根据实体参数删除数据的方法:

HibernateTemplate().delete(entity);

 

public void delete(final Object entity, final LockMode lockMode) throws DataAccessException {
		executeWithNativeSession(new HibernateCallback<Object>() {
			public Object doInHibernate(Session session) throws HibernateException {
				checkWriteOperationAllowed(session);
				if (lockMode != null) {
					session.lock(entity, lockMode);
				}
				session.delete(entity);
				return null;
			}
		});
	}

这是实现的源码,此处只是把实体从session中删掉,等待事务提交时统一生成sql语句。

 


 

 

 

所以这种方式不能保证在同一个事务中的sql执行顺序,假设有这样的代码在程序中:

 

需要在事务中先执行删除,再执行插入。在提交事务时,就发生主键冲突的错误。

经查,hibernate在向db提交缓存的sql时,是按insert、update、delete的顺序提交。这就导致在提交insert语句时出错。 

 

rentDeliveryChargeDao.delete(rentDeliveryCharge);

rentDeliveryKeyDao.delete(rentDeliveryKey);

 

rentDeliveryChargeDao.save(deliveryCharge);

rentDeliveryKeyDao.save(rentDeliveryKey);

 

如果此时有数据库唯一性限制的话,程序可能会报错,主要是由于在同一个事务中不保证sql执行顺序。

 

遇到此类问题应改为以下:

 

rentDeliveryChargeDao.deleteByRentContractId(rentContractId);

 

rentDeliveryKeyDao.delDeliveryKeyByContractId(rentContractId);

后者是底层调用:

public int bulkUpdate(final String queryString, final Object... values) throws DataAccessException {
		return executeWithNativeSession(new HibernateCallback<Integer>() {
			public Integer doInHibernate(Session session) throws HibernateException {
				Query queryObject = session.createQuery(queryString);
				prepareQuery(queryObject);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						queryObject.setParameter(i, values[i]);
					}
				}
				return queryObject.executeUpdate();
			}
		});
	}

 

这样就能保证事务中sql的执行顺序。

 

<think>好的,我现在需要回答用户关于Hibernate的EmptyInterceptor使用实体监听的问题。首先,我得回忆一下Hibernate中的拦截器机制。记得Interceptor接口允许在持久化过程中拦截各种事件,而EmptyInterceptor是其适配器类,用户可以选择覆盖需要的方法。 接下来,用户提到的实体监听可能是指通过拦截器来监听实体的状态变化,比如保存、更新、删除或加载等操作。需要明确EmptyInterceptor和实体监听器(Entity Listeners)之间的区别。实体监听器通常使用注解如@PrePersist,而EmptyInterceptor更底层,适用于全局拦截,可以处理多个实体。 然后,我需要整理EmptyInterceptor的常用方法,比如onSave、onFlushDirty、onLoad、onDelete等,并举例说明如何继承这个类并覆盖这些方法。例如,在onSave方法中,用户可以在保存实体前修改其属性,或者在onLoad时修改加载后的实体数据。 同时,要注意配置拦截器的步骤,即在Hibernate配置中设置,或者通过SessionFactory获取Session时传入拦截器实例。需要给出具体的配置示例,比如在hibernate.cfg.xml中添加属性,或者在代码中设置。 此外,应该比较EmptyInterceptor和@EntityListeners的适用场景,指出前者适合全局处理,后者更细粒度地绑定到具体实体。还要提到可能的最佳实践,比如日志记录、审计跟踪或数据加密等应用场景。 最后,确保回答结构清晰,分步骤说明,并包含代码示例和配置说明,帮助用户逐步理解和实现。检查是否有LaTeX格式的问题,但根据用户指令,可能不需要,所以重点放在中文的清晰解释和正确代码示例上。</think>Hibernate的`EmptyInterceptor`是用于监听实体操作的核心拦截器,以下是分步解析和使用方法: 一、基础概念 1. `EmptyInterceptor`是`Interceptor`接口的适配器类 2. 提供空实现,开发者只需覆盖需要的方法 3. 可拦截的8个核心生命周期事件: - 实体保存前/后 - 实体更新前/后 - 实体加载时 - 实体删除前/后 - 脏检查时 - 事务结束时 二、实现步骤 1. 继承并实现自定义拦截器: ```java public class AuditInterceptor extends EmptyInterceptor { // 保存操作拦截(返回true会修改实体状态) @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof Auditable) { System.out.println("拦截保存操作:" + entity.getClass().getName()); } return false; } // 更新操作拦截 @Override public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { if (entity instanceof Auditable) { System.out.println("拦截更新操作:" + entity.getClass().getName()); } return false; } } ``` 2. 配置拦截器(XML方式): ```xml <!-- hibernate.cfg.xml --> <session-factory> <property name="hibernate.session_factory.interceptor"> com.example.AuditInterceptor </property> </session-factory> ``` 3. 编程式配置(推荐方式): ```java Configuration cfg = new Configuration(); AuditInterceptor interceptor = new AuditInterceptor(); cfg.setInterceptor(interceptor); SessionFactory sf = cfg.buildSessionFactory(); ``` 三、高级应用场景 1. 数据审计跟踪(记录修改记录) ```java @Override public boolean onFlushDirty(...) { if (entity instanceof Auditable) { currentState[getIndex(propertyNames, "lastModified")] = new Date(); currentState[getIndex(propertyNames, "modifiedBy")] = getCurrentUser(); return true; // 必须返回true才会更新状态 } return false; } ``` 2. 数据加密拦截: ```java @Override public boolean onLoad(...) { if (entity instanceof Encryptable) { decryptFields(state, propertyNames); return true; } return false; } @Override public boolean onSave(...) { if (entity instanceof Encryptable) { encryptFields(state, propertyNames); return true; } return false; } ``` 四、与@EntityListener对比 ``` | 特性 | EmptyInterceptor | @EntityListener | |--------------------|------------------------|-----------------------| | 作用范围 | 全局 | 单个实体类 | | 配置方式 | 编程式/XML | 注解 | | 执行顺序 | 更早(框架层拦截) | 较晚(实体层触发) | | 访问Session能力 | 可直接获取 | 需要通过回调接口 | | 多实体统一处理 | 支持 | 需每个类单独配置 | ``` 五、最佳实践建议 1. 优先使用拦截器的场景: - 需要横切多个实体的公共逻辑 - 需要与Session直接交互的操作 - 低级别SQL/HQL操作拦截 2. 注意事项: - 避免在拦截器中执行业务逻辑 - 注意线程安全问题(建议设置为非全局作用域) - 谨慎处理返回值(返回true会修改持久化状态) 3. 性能优化技巧: ```java // 使用缓存提升属性索引查找效率 private int getIndex(String[] properties, String target) { return Arrays.asList(properties).indexOf(target); } ``` 六、调试技巧 1. 启用Hibernate日志: ```xml <logger name="org.hibernate" level="DEBUG"/> ``` 2. 使用拦截器堆栈(组合多个拦截器): ```java public class CompositeInterceptor extends EmptyInterceptor { private List<Interceptor> interceptors = new ArrayList<>(); public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { boolean modified = false; for (Interceptor interceptor : interceptors) { modified |= interceptor.onSave(entity, id, state, propertyNames, types); } return modified; } // 其他方法同理实现... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值