【问题标题】:Exception: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance例外:拥有的实体实例不再引用具有 cascade="all-delete-orphan" 的集合
【发布时间】:2021-01-09 20:55:57
【问题描述】:

我知道它可能会重复。但我被困住了。

当我尝试更新以下实体时。我得到以下异常

拥有的实体实例不再引用具有 cascade="all-delete-orphan" 的集合:com.sip.nglis.partneruser.entities.UserEntity.userPostNominals;

以下是关于实体和我的逻辑的信息

@Entity
@Table(name = "user", schema = "user_management")
public class UserEntity extends BaseEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Integer userId;

    @Column(name = "display_code")
    private String displayCode;

    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
    @JoinColumn(name = "reporting_to", referencedColumnName = "user_id")
    private UserEntity reportingTo;

    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
    private Set<UserRoleEntity> userRoles;

    @OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, orphanRemoval = true)
    private Set<UserPostNominalEntity> userPostNominals;

    getters... setters...
}

用户服务:

@Transactional
public void updateUser(UserWithDependenciesDto user){
      modelMapper.addConverter(CommonUtil.BOOLEAN_SHORT_CONVERTER);
    UserEntity updatedUser = modelMapper.map(user.getUser(), UserEntity.class);

    UserEntity userEntity = userDao.findOne(user.getUser().getUserId());

    // Skipping the fields which will be updated accordingly
    modelMapper.typeMap(UserEntity.class, UserEntity.class).addMappings(mapping -> {
        mapping.skip(UserEntity::setUserPostNominals);
        mapping.skip(UserEntity::setUserRoles);
    });

    // replace the entity with updated data
    modelMapper.map(updatedUser, userEntity);

    //Remove Post Nominal
    userEntity.getUserPostNominals().stream().filter(postNominal -> !userDto.getPostNominal().contains(postNominal.getUserPostNominalPK().getPostNominalId())).collect(Collectors.toSet()).forEach(userEntity.getUserPostNominals()::remove);

    // Add Post Nominal
    Set<UserPostNominalEntity> postNominalEntities = new HashSet<>();
    for (Integer postNominal : user.getUser().getPostNominal()) {
        if (!userEntity.getUserPostNominals().stream().anyMatch(postNominalEntity -> postNominalEntity.getUserPostNominalPK().getPostNominalId() == postNominal)) {
            UserPostNominalEntity userPostNominalEntity = new UserPostNominalEntity();
            userPostNominalEntity.setUser(userEntity);
            userPostNominalEntity.getUserPostNominalPK().setPostNominalId(postNominal);
            userPostNominalEntity.getUserPostNominalPK().setUserId(userEntity.getUserId());
            postNominalEntities.add(userPostNominalEntity);
        }
    }
    postNominalEntities.addAll(userEntity.getUserPostNominals());
    if(!postNominalEntities.isEmpty()){
        userEntity.getUserPostNominals().clear();
        userEntity.getUserPostNominals().addAll(postNominalEntities);
    }


    // Remove Roles
    List<Integer> roleIds = userDto.getRoles().stream().map(userRole -> userRole.getRoleUId()).collect(Collectors.toList());
    userEntity.getUserRoles().stream().filter(userRole -> !roleIds.contains(userRole.getRole().getRoleUId())).collect(Collectors.toSet()).forEach(userEntity.getUserRoles()::remove);
    //Add Roles
    Set<UserRoleEntity> userRoles = new HashSet<>();
    for (RoleDto role : user.getUser().getRoles()) {
        if (!userEntity.getUserRoles().stream().anyMatch(userRoleEntity -> userRoleEntity.getRole().getRoleUId() == role.getRoleUId())) {
            UserRoleEntity userRoleEntity = new UserRoleEntity();
            modelMapper.getConfiguration().setSkipNullEnabled(true);
            RoleEntity roleEntity = modelMapper.map(role, RoleEntity.class);
            userRoleEntity.setRole(roleEntity);
            userRoleEntity.setUser(userEntity);
            userRoleEntity.setAddedBy(user.getCreatedBy());
            userRoleEntity.setAddedTimestamp(Timestamp.from(Instant.now()));

            userRoleEntity.getId().setRoleId(roleEntity.getRoleUId());
            userRoleEntity.getId().setUserId(userEntity.getUserId());

            userRoles.add(userRoleEntity);
        }

    }
    userRoles.addAll(userEntity.getUserRoles());
    if(!userRoles.isEmpty()){
        userEntity.getUserRoles().clear();
        userEntity.getUserRoles().addAll(userRoles);
    }

    userDao.update(userEntity);
}

用户存储库:

public UserEntity update(UserEntity user) {
    return entityManager.merge(user);
}

上述异常仅在我尝试更新UserEntity中的reportingTo时发生,如果我在映射过程中尝试跳过它并且不更新reportingTo 然后 上述异常消失。

如果我在服务 updateUser 方法中这样做

modelMapper.typeMap(UserEntity.class, 
UserEntity.class).addMappings(mapping -> {
    mapping.skip(UserEntity::setUserPostNominals);
    mapping.skip(UserEntity::setReportingTo);
    mapping.skip(UserEntity::setUserRoles);
});

由于上述代码完全删除了针对用户更新它的 reportingTo,因此上述异常消失了。

reportingTo 是一个分离的对象,因此该对象具有唯一的 ID,其余字段为空。

【问题讨论】:

  • 当一个人希望父母的变化传播给它的孩子而不是相反时,使用级联 ALL...乍一看,我猜@ManyToOne 关系中的cascade all首先不应该在那里......见stackoverflow.com/questions/13027214/…
  • @francisconeto 让我检查级联合并。如果有效,将返回
  • @francisconeto 谢谢。那是罪魁祸首。我完全从reportingTo 中删除了级联。我觉得有点愚蠢,为什么我还要这样做。尽管在所有其他地方,我没有在具有多对一关系的子实体上放置级联。请移动您的评论以回答,以便我标记它。
  • 很高兴能帮上忙

标签: java hibernate spring-data-jpa


【解决方案1】:

CascadeType.ALL 用于当人们希望父母的变化传播给其孩子而不是相反时......

因此,在@OneToMany 关系中使用cascade = {CascadeType.ALL} 是一种常见模式,但在@ManyToOne 关系中使用它却非常不寻常。

乍一看,我猜想@ManyToOne reportingTo 关系中的CascadeType.ALL 首先不应该存在...

在其他问题中,您可能会找到有关@ManyToOne 和级联的有用信息: What is the meaning of the CascadeType.ALL for a @ManyToOne JPA association

【讨论】:

    猜你喜欢
    • 2012-03-14
    • 2011-08-01
    • 1970-01-01
    • 2018-03-06
    • 2016-11-26
    • 2023-03-17
    • 2018-10-08
    • 2020-05-28
    相关资源
    最近更新 更多