jpa遇到的 org.hibernate.PersistentObjectException: detached entity passed to persis

本文解决了使用JPA+Spring+Struts2进行开发时,出现的PersistentObjectException异常问题。详细介绍了异常产生的原因及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文转载自 http://blog.youkuaiyun.com/zhoutao198712/archive/2009/05/14/4184517.aspx
    再用jpa+spring+struts2开发的是时候遇到一个问题(采用了注解的方式,xml配置的道理是一样的),当我在注册用户的时候,注册第一个用户没有问题,但注册第二个用户开始就会抛出一个异常:


javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.dalton.domain.User

org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException

(AbstractEntityManagerImpl.java:614)
    org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:226)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        …………………………

在网上找了半天也没找到答案。

后来通过println找了原因,当我第二次插入用户的时候,还没有实例化到数据库就有了ID值,而且这个ID值是我第一次插入用户的ID,这是不正常,因为ID没有设置过,而且JPA默认的行为是只要指定了主键生成策略,主键就不能设置了,一旦不为空或者0就被认为是已经保存到了数据库中,一旦调用persist()方法就会抛出上面的异常。

为什么会还有第一次的插入用户的信息呢,原因就是我action的作用域设置错误了,我采用了默认单例的设置,对于Action来说必须采用 prototype(每次调用创建一个对象)的作用域,修改方法是:在Action上就一个注解@Scope("prototype")

这样就OK了。
<think>我们遇到的问题是:在尝试使用Hibernate的`persist`方法时,抛出了异常,提示"detachedentitypassedtopersist"。这意味着我们试图持久化的实体处于游离状态(detached),而不是一个新的实体(transient)。###问题分析:在Hibernate中,实体对象有以下几种状态:1.**瞬时状态(Transient)**:对象由new操作符创建,且尚未与HibernateSession关联。它没有持久化标识(通常是主键)。2.**持久状态(Persistent)**:对象已经被保存,且在当前的Session作用域内。任何对持久状态对象的修改都会被Hibernate检测到,并在刷新Session时同步到数据库。3.**游离状态(Detached)**:对象曾经与某个Session关联,但由于Session被关闭或对象被清除(evict),而不再与Session关联。但是,它可能有一个持久化标识(即主键)。4.**删除状态(Removed)**:对象已经被标记为删除,将在Session刷新时从数据库中删除。`persist`方法用于将一个新的瞬时状态实体转变为持久状态。如果传入一个游离状态的实体(即该实体已经有一个标识符,并且可能之前已经持久化过),那么Hibernate会抛出`PersistentObjectException`。###常见原因:1.你试图持久化一个已经存在于数据库中的实体(即该实体已经有一个主键值)。2.你可能错误地给实体设置了一个主键值(例如,手动设置了一个ID),而Hibernate期望这个ID由数据库生成(比如自增)。这样,Hibernate会认为这个实体是游离的(因为它有ID),而不是瞬时的。3.你可能从另一个Session中获取了这个实体,然后尝试在新的Session中持久化它。###解决方案:1.**检查实体状态**:确保你要持久化的实体是一个新的实体,没有设置主键(如果主键是自动生成的话)。如果你是从其他Session中获取的实体,那么应该使用`merge`方法而不是`persist`。2.**使用`merge`方法**:如果你想要将一个游离状态的实体重新关联到当前Session,应该使用`merge`方法。它会将游离状态实体的状态复制到持久化状态的实体上(如果存在),或者创建一个新的持久化实体(如果不存在)。3.**检查主键生成策略**:确认你的实体主键生成策略正确。如果主键是自动生成的(如`@GeneratedValue`),那么你不应该手动设置主键值。###示例代码:假设我们有一个实体类`Caizhi`,主键由数据库自动生成。**错误示例**:```java//错误示例:手动设置id,然后尝试persistCaizhicaizhi=newCaizhi();caizhi.setId(1L);//手动设置idsession.persist(caizhi);//这里会抛出异常,因为设置了id,Hibernate认为它是游离的```**正确做法**:1.不要手动设置id(如果使用自动生成):```javaCaizhicaizhi=newCaizhi();//不设置id,让数据库自动生成caizhi.setName("SomeName");session.persist(caizhi);//正确```2.如果实体已经存在(比如从另一个Session中加载),使用`merge`:```javaCaizhidetachedCaizhi=...;//从其他地方获取的游离实体//在新的Session中CaizhipersistentCaizhi=session.merge(detachedCaizhi);//使用merge```###总结:-当实体是新的(没有设置主键)时,使用`persist`。-当实体是游离的(有主键,但不与当前Session关联)时,使用`merge`。###相关问题:根据问题,我们可以提出以下相关问题:§§相关问题§§:1.Hibernate中实体对象的几种状态是什么?2.Hibernatepersist方法和merge方法有什么区别?3.如何解决Hibernate中的"detachedentitypassedtopersist"异常?4.在SpringDataJPA中,save方法和saveAndFlush方法有什么区别?</think>### 问题分析: 这个错误发生在使用 Hibernate/JPA 的 `persist()` 方法时,试图持久化一个 **游离状态(detached)** 的实体。根本原因是实体已经拥有持久化标识(ID),但当前 Session 不再管理它。以下是详细解释和解决方案: --- ### 解决方案: #### 1. 使用 `merge()` 代替 `persist()` `persist()` 只适用于 **瞬时状态(transient)** 实体(无 ID),而 `merge()` 适用于游离实体(有 ID 但未关联 Session)。 ```java // 错误方式:persist() 用于游离实体 // entityManager.persist(caizhi); // 正确方式:使用 merge() 处理游离实体 Caizhi managedCaizhi = entityManager.merge(caizhi); ``` #### 2. 检查实体 ID 来源 确保实体不是通过以下方式获取的: - 从其他 Session 加载的实体 - 手动设置了 ID 的“新”实体 - 反序列化得到的实体 #### 3. 验证级联操作配置 如果实体是其他实体的关联对象,检查级联配置是否正确: ```java @Entity public class ParentEntity { @OneToMany(cascade = CascadeType.ALL) // 确保包含 CascadeType.PERSIST private List<Caizhi> caizhis; } ``` #### 4. 检查事务边界 确保操作在同一个事务和 Session 作用域内完成: ```java @Service public class YourService { @Transactional // 确保方法有事务注解 public void saveCaizhi(Caizhi caizhi) { entityManager.merge(caizhi); // 在事务内操作 } } ``` --- ### 关键概念解释: | 实体状态 | 特征 | 正确操作方法 | |----------------|-------------------------------|-----------------| | **瞬时状态** | 新对象,无 ID | `persist()` | | **持久状态** | 已关联 Session,有 ID | 自动跟踪变更 | | **游离状态** | 有 ID,但未关联当前 Session | `merge()` | | **删除状态** | 被标记删除 | `remove()` | --- ### 完整示例代码: ```java // 正确保存游离实体的服务层代码 @Service public class CaizhiService { @PersistenceContext private EntityManager entityManager; @Transactional public void saveOrUpdate(Caizhi caizhi) { if (caizhi.getId() == null) { entityManager.persist(caizhi); // 新对象用 persist } else { entityManager.merge(caizhi); // 已存在对象用 merge } } } ``` --- ### 预防措施: 1. **避免跨 Session 传递实体**:在展示层使用 DTO 而非实体对象 2. **检查 ID 生成策略**: ```java @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 正确配置ID生成 private Long id; ``` 3. **使用 `@Version` 乐观锁**:防止并发修改问题 4. **在事务边界内操作**:确保整个操作在单个事务中完成 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值