JPA OneToOne映射保存实体报行被其他事务更新/删除错误
问题根因
这个异常是JPA双向关联配置和持久化逻辑不匹配导致的,核心触发点有三个:
- 双向1对1关联的级联逻辑和手动保存冲突:
InvitationLink作为外键持有端(配置了@JoinColumn)设置了CascadeType.ALL,调用invitationLinkRepository.save()时会自动级联保存关联的瞬态Team对象,后续手动调用teamRepository.save(team)属于重复持久化同一个实体,JPA持久化上下文检测到实体状态和数据库预期不一致,就会抛出该异常。 - Lombok
@Data注解自动生成的toString、equals、hashCode方法会遍历关联字段,双向关联下会出现无限递归调用,也就是你之前碰到的递归报错。 - 若UUID主键生成策略未明确指定,JPA可能无法正确判断实体是否为未保存状态,触发unsaved-value映射错误。
修复方案
按以下步骤调整即可解决问题:
- 处理双向关联的递归问题:给两个实体的双向关联字段添加Lombok排除注解,避免生成方法时循环调用。
- 简化持久化逻辑:删除重复的
teamRepository.save(team)调用,级联配置会自动完成关联Team对象的持久化,不需要手动重复保存。 - (可选,若仍报unsaved-value错误)明确指定UUID主键的生成策略,避免JPA状态判断错误。
修正后代码
Team实体
@Table(name = "team") @Entity @AllArgsConstructor @NoArgsConstructor @Data @With public class Team { @Id @Column @GeneratedValue private UUID id; @Column private String teamName; @OneToMany(cascade = CascadeType.ALL) private List<TeamMember> teamMembers; // 排除双向关联字段,避免递归 @ToString.Exclude @EqualsAndHashCode.Exclude @OneToOne(mappedBy = "team") private InvitationLink invitationLink; }
InvitationLink实体
@Entity @Data @With @NoArgsConstructor @AllArgsConstructor @Table(name = "invitationLink") public class InvitationLink { @Id @Column @GeneratedValue private UUID id; // 排除双向关联字段,避免递归 @ToString.Exclude @EqualsAndHashCode.Exclude @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "team_id", referencedColumnName = "id") private Team team; @Column private String invitationNumbers; }
业务保存逻辑
@Override @Transactional public void createTeam(UserDto userDto, TeamDto teamDto) throws JSONException { // 此处省略无关业务代码 InvitationLink invitationLink = new InvitationLink(); invitationLink.setInvitationNumbers(StringUtils.generateShortUUID()); // 正确设置双向关联 invitationLink.setTeam(team); team.setInvitationLink(invitationLink); // 外键持有端单次保存即可,级联自动持久化关联的Team invitationLinkRepository.save(invitationLink); // 非级联管理的实体正常单独保存 userRepository.save(user); // 如果teamMember已经加入team的teamMembers列表,这行可以省略,OneToMany的级联会自动保存 teamMemberRepository.save(teamMember); }
注意:如果调整后仍触发unsaved-value相关报错,给两个实体的主键字段替换UUID生成策略即可:
@Id @Column @GeneratedValue(generator = "uuid2") @GenericGenerator(name = "uuid2", strategy = "uuid2") private UUID id;
内容的提问来源于stack exchange,提问作者Toms
相关产品推荐
相关产品推荐

