You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot JPA报错:detached entity passed to persist 求助

问题:OneToOne关联创建用户时抛出PersistentObjectException异常

有JPA基础,正在学习Spring Boot,开发基于MySQL的简单Web应用,创建了users和users_session_data两张表,二者为OneToOne关联(每个用户对应一条会话数据),但创建用户时抛出如下异常:

org.hibernate.PersistentObjectException: detached entity passed to persist: com.simpleapp.entities.EntityUsersSessionData
    at org.hibernate.event.internal.DefaultPersistEventListener.persist(DefaultPersistEventListener.java:88)
    ...(省略后续堆栈信息)

现有代码

BaseEntity

@MappedSuperclass
abstract class BaseEntity : java.io.Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Basic(optional = false)
  @Column(name = "ID", nullable = false)
  open var id: Int? = null

  @Basic(optional = false)
  @Column(name = "STATUS", nullable = false)
  @Enumerated(EnumType.STRING)
  open var status: DBEntryStatusTypeEnum = DBEntryStatusTypeEnum.ACTIVE

  ...(省略其余代码)
}

BaseUserEntity

@MappedSuperclass
open class BaseUserEntity : BaseEntity() {

  @Id
  @Basic(optional = false)
  @Column(name = "ID", nullable = false)
  @JsonIgnore
  @XmlTransient
  override var id: Int? = null

  @JsonIgnore
  @XmlTransient
  @JoinColumn(name = "ID", referencedColumnName = "ID", nullable = false, insertable = false, updatable = false)
  @OneToOne(optional = false, fetch = FetchType.LAZY)
  open var users: EntityUsers? = null

}

EntityUsers

@Entity
@Table(name = "users")
@XmlRootElement
class EntityUsers : BaseEntity() {

  @Column(name = "NICK", length = 255)
  var nick: String? = null

  ...(省略其余代码)

  @XmlTransient
  @JsonIgnore
  @OneToOne(cascade = [CascadeType.ALL], mappedBy = "users", fetch = FetchType.LAZY)
  var usersSessionData: EntityUsersSessionData? = null

}  

EntityUsersSessionData

@Entity
@Table(name = "users_session_data")
@XmlRootElement
class EntityUsersSessionData : BaseUserEntity() {

  @Basic(optional = false)
  @Column(name = "TOKEN", nullable = false, length = 255)
  var token: String? = null

}

FlowUsersSessions

@Component
class FlowUsersSessions {

  ...(省略其余代码)

  fun create(user: EntityUsers): EntityUsersSessionData? {
    val record = EntityUsersSessionData().apply {
      id = user.id
      status = user.status
      createdAt = Date()
      token = UUID.randomUUID().toString()
    }
    return getDao().createRecord(record)
  }
}  

异常原因分析

  1. 手动设置主键触发游离实体判定:创建EntityUsersSessionData时直接赋值id = user.id,这个id是已存在于数据库的主键值。Hibernate的persist方法仅用于保存瞬时态实体(主键为null、未被持久化过的实体),当实体已有主键值时,Hibernate会判定这是游离态(detached)实体,因此抛出异常。
  2. 共享主键关联配置错误:两张表是共享主键的OneToOne关联(users_session_data的ID既是主键也是关联users表的外键),但当前配置存在问题:
    • BaseUserEntity重复定义id,导致主键映射冲突;
    • 未使用@MapsId注解指定主键来源,手动设置id违反JPA主键生成逻辑;
    • 关联配置中insertable = false, updatable = false导致无法通过维护端维护关联关系。

解决办法

1. 修正共享主键关联配置

修改BaseUserEntity,去掉重复的id定义,添加@MapsId注解指定主键来源:

@MappedSuperclass
open class BaseUserEntity : BaseEntity() {

  @JsonIgnore
  @XmlTransient
  @OneToOne(optional = false, fetch = FetchType.LAZY)
  @MapsId // 指定当前实体的ID从关联的users实体获取
  @JoinColumn(name = "ID", referencedColumnName = "ID", nullable = false)
  open var users: EntityUsers? = null

}

2. 修改会话数据创建逻辑

不再手动设置id,而是维护实体关联关系,利用cascade特性自动持久化:

@Component
class FlowUsersSessions {

  ...(省略其余代码)

  fun create(user: EntityUsers): EntityUsersSessionData? {
    val record = EntityUsersSessionData().apply {
      users = user // 建立与用户的关联
      status = user.status
      createdAt = Date()
      token = UUID.randomUUID().toString()
    }
    // 关联用户的会话数据,借助cascade=ALL自动持久化
    user.usersSessionData = record
    daoFactory.getUsersDao().updateRecord(user)
    return record
  }
}  

3. 验证事务配置

确保FlowUsersSessions.create方法或调用它的方法被Spring事务管理(比如添加@Transactional注解),避免因会话关闭导致关联实体持久化失败。

内容的提问来源于stack exchange,提问作者Ali Yucel Akgul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 16:35:06