OneToOne关联中CascadeType.ALL不生效,保存时抛出瞬时实例异常
问题描述
实现User与Team的OneToOne关联(一个User对应一个Team)时,保存User无法自动级联创建Team,抛出TransientPropertyValueException异常。
实体代码
User 实体
@Entity @Table(name = "USER") @Data @NoArgsConstructor @AllArgsConstructor @Builder public class User implements Serializable { private static final long serialVersionUID = 3233149207833106460L; @Id @GeneratedValue @Column(name = "USERID") private Long id; ... @OneToOne(mappedBy = "user") private Team team; }
Team 实体
@Entity @Table(name = "TEAM") @Data @NoArgsConstructor @AllArgsConstructor @Builder public class Team implements Serializable { private static final long serialVersionUID = 3233149207833106460L; @Id @GeneratedValue @Column(name = "TEAMID") private Long id; ... @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "USERID") private User user; }
保存逻辑代码
// 创建Team并关联到User Team team = Team .builder() .value(TeamConstants.INITIAL_VALUE) .name(userDetailsForm.getUsername()+" "+TeamConstants.TEAM) .country(userDetailsForm.getUsername()+" "+TeamConstants.COUNTRY) .build(); User user = User .builder() .username(userDetailsForm.getUsername()) .password(this.passwordEncoder.encode(userDetailsForm.getPassword())) .email(userDetailsForm.getEmail()) .registerDate(DateUtil.getNow()) .role(userDetailsForm.getRole()) .team(team) .build(); team.setUser(user); this.userRepository.save(user);
抛出异常
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.soccermanager.business.entities.user.User.team -> com.soccermanager.business.entities.team.Team;
解决方案
问题根源是级联配置的位置不对:当前CascadeType.ALL配置在Team的user字段上,意味着只有保存Team时才会级联保存关联的User。但你是从User端发起保存,User的team字段未配置级联,Hibernate无法自动处理关联的Team实例。
方式一:给User的@OneToOne添加级联配置
修改User实体中team字段的注解,补充级联配置:
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL) private Team team;
这样调用userRepository.save(user)时,Hibernate会自动级联保存关联的Team。
方式二:调整保存顺序,从Team端发起保存
如果不想修改实体注解,可以先保存Team,利用Team的级联配置保存User:
// 创建Team并关联User Team team = Team.builder() .value(TeamConstants.INITIAL_VALUE) .name(userDetailsForm.getUsername()+" "+TeamConstants.TEAM) .country(userDetailsForm.getUsername()+" "+TeamConstants.COUNTRY) .build(); User user = User.builder() .username(userDetailsForm.getUsername()) .password(this.passwordEncoder.encode(userDetailsForm.getPassword())) .email(userDetailsForm.getEmail()) .registerDate(DateUtil.getNow()) .role(userDetailsForm.getRole()) .build(); team.setUser(user); this.teamRepository.save(team); // 保存Team时自动级联保存User
补充说明
OneToOne关联中,mappedBy标记当前实体为关联的被动方(非外键拥有方),Team是拥有方(@JoinColumn定义了外键存储)。级联操作需要配置在发起保存的一端,或者从拥有方发起保存并利用其级联配置。
内容的提问来源于stack exchange,提问作者Antonio

