EclipseLink持久化User实体报错:一对一关联未配置Cascade PERSIST
问题场景
数据库中用户表(users)与用户角色表(userroles)为一对一关联,外键为userroles表的user_id字段。使用EclipseLink持久化User实体时,触发如下报错:
Error saving entity User{id=0, username=tom.black@mydomain.it, email=tom.black@mydomain.it, password=mypassword, userRole=UserRole(id=null, role=admin, userId=0)}
jakarta.persistence.RollbackException: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: UserRole(id=null, role=admin, userId=0).
原实体类与持久化代码
User实体类
@Entity @Table(name = "users") @Data @XmlRootElement public class User implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "id") private Integer id; @Basic(optional = false) @NotNull @Size(min = 1, max = 45) @Column(name = "username") private String username; // @Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation @Basic(optional = false) @NotNull @Size(min = 1, max = 128) @Column(name = "email") private String email; @Basic(optional = false) @NotNull @Size(min = 1, max = 128) @Column(name = "password") private String password; @OneToOne(mappedBy = "user", optional = false, fetch = FetchType.EAGER) private UserRole userRole; }
UserRole实体类
@Entity @Table(name = "userroles") @Data @ToString(doNotUseGetters = true) public class UserRole implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "id") private Integer id; @Size(max = 45) @Column(name = "role") private String role; @Basic(optional = false) @NotNull @Column(name = "user_id") private int userId; @OneToOne(fetch = FetchType.EAGER, optional = false, cascade = CascadeType.ALL) @JoinColumn(name = "user_id", insertable=false, updatable=false) @ToString.Exclude private User user; }
原持久化save方法
... save(aUser); .. public void save(Object entity) throws Exception { EntityManager em = getFactory().createEntityManager(); try { EntityTransaction t = em.getTransaction(); try { t.begin(); em.persist(entity); t.commit(); } catch (Exception e) { throw new Exception("Error saving entity " + entity, e); } finally { if (t.isActive()) { t.rollback(); } } } finally { em.close(); } }
项目同时使用Lombok库。
解决方法
通过调整实体类关联配置并修复双向关联维护逻辑,解决了报错问题,具体修改如下:
1. User实体类更新
为userRole字段添加级联持久化配置,确保持久化User时自动触发UserRole的持久化:
@OneToOne(mappedBy = "user", optional = false, orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) private UserRole userRole;
2. UserRole实体类更新
删除冗余的userId基本字段(避免与关联注解重复映射user_id字段),同时调整@OneToOne注解的属性,让关联字段负责外键的插入与更新:
@Entity @Table(name = "userroles") @Data @ToString(doNotUseGetters = true) public class UserRole implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "id") private Integer id; @Size(max = 45) @Column(name = "role") private String role; @OneToOne(optional = false, fetch = FetchType.EAGER) @JoinColumn(name = "user_id", insertable=true, updatable=true) @ToString.Exclude private User user; }
3. 持久化代码更新
在持久化User前,手动维护双向关联关系,确保ORM能识别实体间的关联:
newUser.getUserRole().setUser(newUser); // 新增此行,建立双向关联 em.persist(newUser);
内容的提问来源于stack exchange,提问作者afterbit

