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) } }
异常原因分析
- 手动设置主键触发游离实体判定:创建
EntityUsersSessionData时直接赋值id = user.id,这个id是已存在于数据库的主键值。Hibernate的persist方法仅用于保存瞬时态实体(主键为null、未被持久化过的实体),当实体已有主键值时,Hibernate会判定这是游离态(detached)实体,因此抛出异常。 - 共享主键关联配置错误:两张表是共享主键的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
相关产品推荐
相关产品推荐

