Exception: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance

huangapple go评论142阅读模式
英文:

Exception: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance

问题

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

当我尝试更新下面的实体时。我得到了下面的异常

级联关系为 "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...

}

UserService:

@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());

// 跳过相应将根据需要更新的字段
modelMapper.typeMap(UserEntity.class, UserEntity.class).addMappings(mapping -> {
    mapping.skip(UserEntity::setUserPostNominals);
    mapping.skip(UserEntity::setUserRoles);
});

// 用更新的数据替换实体
modelMapper.map(updatedUser, userEntity);

//删除后置名
userEntity.getUserPostNominals().stream().filter(postNominal -> !userDto.getPostNominal().contains(postNominal.getUserPostNominalPK().getPostNominalId())).collect(Collectors.toSet()).forEach(userEntity.getUserPostNominals()::remove);

// 添加后置名
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);
}

// 删除角色
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);
// 添加角色
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);

}

UserRepository:

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,其余字段为空。

英文:

I know it may be duplicated. But I'm stuck.

When I try to update the below entity. I get the below exception

A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: com.sip.nglis.partneruser.entities.UserEntity.userPostNominals;

Below is the information regarding Entities and my logic

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

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

    @Column(name = &quot;display_code&quot;)
    private String displayCode;

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

    @OneToMany(mappedBy = &quot;user&quot;, cascade = {CascadeType.ALL}, orphanRemoval = true)
    private Set&lt;UserRoleEntity&gt; userRoles;

    @OneToMany(mappedBy = &quot;user&quot;, cascade = {CascadeType.ALL}, orphanRemoval = true)
    private Set&lt;UserPostNominalEntity&gt; userPostNominals;

    getters... setters...
}

UserService:

@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 -&gt; {
        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 -&gt; !userDto.getPostNominal().contains(postNominal.getUserPostNominalPK().getPostNominalId())).collect(Collectors.toSet()).forEach(userEntity.getUserPostNominals()::remove);

    // Add Post Nominal
    Set&lt;UserPostNominalEntity&gt; postNominalEntities = new HashSet&lt;&gt;();
    for (Integer postNominal : user.getUser().getPostNominal()) {
        if (!userEntity.getUserPostNominals().stream().anyMatch(postNominalEntity -&gt; 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&lt;Integer&gt; roleIds = userDto.getRoles().stream().map(userRole -&gt; userRole.getRoleUId()).collect(Collectors.toList());
    userEntity.getUserRoles().stream().filter(userRole -&gt; !roleIds.contains(userRole.getRole().getRoleUId())).collect(Collectors.toSet()).forEach(userEntity.getUserRoles()::remove);
    //Add Roles
    Set&lt;UserRoleEntity&gt; userRoles = new HashSet&lt;&gt;();
    for (RoleDto role : user.getUser().getRoles()) {
        if (!userEntity.getUserRoles().stream().anyMatch(userRoleEntity -&gt; 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);
}

UserRepository:

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

The above exception only occurs When I try to update the reportingTo in UserEntity, If I try to skip it during mapping and don't update the value of reportingTo then
the above exception goes away.

If I do like this in the service updateUser method

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

As the above code totally removes reportingTo From updating it against the user, then the above-mentioned exception goes away.

reportingTo is a detached object so the object has the only ID the remaining fields are empty.

答案1

得分: 1

A CascadeType.ALL 是在一个希望父级的更改传播到其子级而不是反过来时使用的...

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

乍一看,我会猜测@ManyToOne中的reportingTo关系中不应该有CascadeType.ALL...

在另一个问题中,您可能会找到有关@ManyToOne和级联的有用信息:
https://stackoverflow.com/questions/13027214/what-is-the-meaning-of-the-cascadetype-all-for-a-manytoone-jpa-association

英文:

A CascadeType.ALL is used when one wants that the changes in parents propagate to its children not the other way round...

So, it is a common pattern using cascade = {CascadeType.ALL} in @OneToMany relationships but very unusual to use it on @ManyToOne relationships.

At a first glance I would guess that the CascadeType.ALL in the @ManyToOne reportingTo relationship should not be there in first place...

In that other question you may find usefull information about @ManyToOne and cascading:
https://stackoverflow.com/questions/13027214/what-is-the-meaning-of-the-cascadetype-all-for-a-manytoone-jpa-association

huangapple
  • 本文由 发表于 2020年9月23日 23:44:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/64031637.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定