JPA+Hibernate出错java.lang.IllegalArgumentException: Removing a detached instance

JPA批量删除

JPA+Hibernate Junit4做批量删除时出错:

批量删除方法如下:

private EntityManager em

 

	/**
	 * 批量删除实体
	 * 可实体批量删除操作,在一个transaction中完成
	 * 任何Exception发生,全部更新操作回滚
	 * @param entityClass 实体类
	 * @param entityids 实体id数组
	 */
	public void batchDelete(List<T> entities){
		for(int i = 0; i<entities.size(); i++){
			em.remove(entities.get(i));
		}
	}

 junit测试出现以下错误:

java.lang.IllegalArgumentException: Removing a detached instance com.agiliti.bean.person.Person#47

..................

原因:

em调用remove时,Hibernate仍然还处于“detach”状态,在Hibernate文档中关于detach叙述:

Detached - a detached instance is an object that has been persistent, but its Session has been closed. The reference to the object is still valid, of course, and the detached instance might even be modified in this state. A detached instance can be reattached to a new Session at a later point in time, making it (and all the modifications) persistent again. 

此时,Session已经关闭,但引用仍然存在(换句话说,heap里的对象没有了)。所以此时必须再次产生一个Session(在heap里产生对象):

解决方法之一——在删除之前把这个Detached instance绑定到当前的Sesssion,在用当前Sesssion删除此instance。getEntityManager()提供merge方法实现:

 

public void batchDelete(List<T> entities){
		for(int i = 0; i<entities.size(); i++){
			em.remove(em.merge(entities.get(i)));
		}
	}
 

 

请注意:如果写成:

仍然错误写法

	public void batchDelete(List<T> entities){
		for(int i = 0; i<entities.size(); i++){
			em.merge(entities.get(i));
                        em.remove(entities.get(i));
		}
	}

 原因是执行完merge还是detached, merge后返回的新对象才是允许删除的。

 

 

 

 

在使用 HibernateJPA 进行实体删除操作时,出现 `java.lang.IllegalArgumentException: Removing a detached instance` 异常,通常是因为尝试直接删除一个处于“分离”(detached)状态的实体对象。该对象虽然可能具有持久化数据的标识(如主键),但由于其当前不在持久化上下文中(即未与 Session 或 EntityManager 关联),Hibernate/JPA 无法识别它为有效的托管实例[^1]。 ### 原因分析 - **分离状态**:对象是在事务或会话之外创建或获取的,因此不与当前的 Session 或 EntityManager 绑定。 - **直接使用分离对象进行删除**:当调用 `remove()` 方法并传入一个分离对象时,JPA/Hibernate 无法识别该对象的状态,从而抛出异常[^3]。 ### 解决方案 #### 方法一:通过主键重新加载实体 确保在执行删除操作前,从数据库中重新加载该实体,使其处于托管状态(managed): ```java Entity entity = entityManager.find(Entity.class, id); if (entity != null) { entityManager.remove(entity); } ``` 此方法保证了被删除的对象是当前 Session/EntityManager 中的托管对象[^3]。 #### 方法二:合并后再删除(Merge and Remove) 如果只有分离状态的对象可用,可以通过 `merge()` 方法将其状态同步到一个新的托管实例上,再执行删除操作: ```java Entity mergedEntity = entityManager.merge(detachedEntity); entityManager.remove(mergedEntity); ``` 这种方法适用于需要对分离对象本身进行操作的情况,但要注意合并可能导致额外的数据访问开销[^4]。 #### 方法三:使用原生 SQL 或 JPQL 删除语句 若仅需根据 ID 删除记录而无需操作实体对象本身,可考虑使用 JPQL 或原生 SQL 直接执行删除操作: ```java String jpql = "DELETE FROM Entity e WHERE e.id = :id"; entityManager.createQuery(jpql) .setParameter("id", entityId) .executeUpdate(); ``` 这种方式绕过了实体状态管理机制,适合批量删除或轻量级操作。 ### 最佳实践建议 - 在 Web 应用中,避免将实体对象从服务层传递到界面层再回传至删除逻辑,应尽量使用 ID 而非完整对象进行后续操作。 - 确保删除操作始终在事务内执行,并且作用于当前 Session/EntityManager 上下文中的对象。 - 使用 DTO(Data Transfer Object)模式来传输非实体数据,减少实体对象在不同层之间的传递风险。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值