How to delete assignment from @ManyToMany relation?

时间:2015-11-17 16:43:28

标签: java hibernate hibernate-mapping

I have a problem with deleting (by unassign only) roles which are assigned to user through association table. Only assignment should be deleted.

I need to have a User entity with relation ManyToMany to Rolle entity but this relation (assignment) needs to have additional information like expiriation_date (because assignment can expire). So, I cannot use automatically created and managed association table by hibernate (@JoinTable - which was working really great when I didn't need an additional column) but now I need to extend the association with an extra column and that's why instead of that @JoinTable I need to create such a association table manualy. Below my model relation which is working when I am adding some roles to user, but not working when I try to delete some role.

enter image description here

USER:

@Entity
@Table(name = "USER")
public class User {
    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "NAME")
    private String name;

    @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<UserRole> userRoleAssignments = new LinkedHashSet<>();

    //getters, setters

    public List<BrugersystemrolleDB> getRoles() {
        return this.userRoleAssignments.stream().
            map(roleAssignment -> roleAssignment.getRole()).
            collect(Collectors.toList());
    }

    public void setRoles(List<Role> roles) {
        this.userRoleAssignments = roles.stream().
            map(role -> useExistingOrNewRoleAssignment(role)).
            collect(Collectors.toSet()));
    }

    private UserRole useExistingOrNewRoleAssignment(Role role) {
        Optional<Role> roleAssignmentOpt = getRoleAssignment(role);
        if (roleAssignmentOpt.isPresent()) {
            return roleAssignmentOpt.get();
        } else {
            return new UserRole(role, this);
        }
    }

    private Optional<UserRole> getRoleAssignment(Role role) {
        return roleAssignments.stream().
                filter(roleAssignment ->
                        roleAssignment.getRole().getId() == role.getId()).findFirst();
    }
}

ROLE:

@Entity
@Table(name = "ROLE")
public class Role {
    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "NAME")
    private String name;

    @OneToMany(mappedBy = "role", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<UserRole> userRoleAssignments = new LinkedHashSet<>();

    //getters, setters
}

USER_ROLE:

@Entity
@Table(name = "USER_ROLE")
public class UserRole {

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne(cascade = {CascadeType.ALL})
    @JoinColumn(name = "USER_ID")
    private User user;

    @ManyToOne(cascade = {CascadeType.ALL})
    @JoinColumn(name = "ROLE_ID")
    private Role role;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "EXPIRE_DATE", nullable = true)
    private Date expireDate;

    public UserRole (Role role, User user) {
        this.role = role;
        this.user = user;
    }

    //getters, setters
}

用法:

@Stateless
public class UserLogic {

    @EJB private UserDao userDao;
    @EJB private UserRoleDao userRoledao;

    //... just some generic solution for update requests
    public User updateUser(User user, UserVo, userVo) {
        user.setName(userVo.getName());
        assignRollesToUser(user, userVo.getRoles());
        return userDao.update(user); // <-- is causing an error when some role is deleted!
    }

    // this method is responsible for roles management and is causing a problem during update user list of roles, but only when some of them needs to be deleted.
    public void assignRollesToUser(User user, List<Integer> roles) {
        // 1 step: remove all existing assignments:
        user.getRoleAssignments().stream().
                forEach(roleAssignment -> {
                    // 1) solution with userRoleDao is causing a problem with deleted/detached object passed to merge:
                    userRoleDao.delete(roleAssignment);
                    // 2) solution with removing role from list gives me an error saying: NULL not allowed for column 'USER_ID' which is true, but why hibernate is not deleting this entry and wants to to set null for user_id which breaks a constraint?
                    // user.getRoleAssignments().remove(userAssignment);
                });
        // 2 step: assign new set of roles
        List<Role> newRolesToAssign = getRolesFromDB(roles);
        user.setRoles(newRolesToAssign )
    }

    private List<Role> getRolesFromDB(List<Integer> rolles) {
        return rolles.stream().
                map(userRoleDao::read).
                collect(Collectors.toList());
    }
}

我可以向用户添加新角色,但无法删除任何角色,当尝试删除时,我正在接收

  

已删除的实例已传递给合并

  

传递给合并的分离实例

取决于删除方式。我尝试通过dao.delete()删除角色,或者只删除收集而不删除dao。两种解决方案都不起作用。你能告诉我一些我做错了什么吗?我缺少什么吗?

编辑:

低于在UserLogic中将forEach更改为:

后出现的错误
forEach(roleAssignment -> {
    user.getRoleAssignments().remove(userAssignment);
    roleAssignment.setUser(null);
    roleAssignment.setRole(null);
});

错误:

[Server:server-one] Caused by: java.util.ConcurrentModificationException
[Server:server-one]     at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429) [rt.jar:1.8.0_45-internal]
[Server:server-one]     at java.util.HashMap$KeyIterator.next(HashMap.java:1453) [rt.jar:1.8.0_45-internal]
[Server:server-one]     at org.hibernate.collection.internal.AbstractPersistentCollection$IteratorProxy.next(AbstractPer
sistentCollection.java:789) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
[Server:server-one]     at java.util.Iterator.forEachRemaining(Iterator.java:116) [rt.jar:1.8.0_45-internal]
[Server:server-one]     at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) [rt.jar:1
.8.0_45-internal]
[Server:server-one]     at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) [rt.jar:1.8.0_45-intern
al]
[Server:server-one]     at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502) [rt.jar:1.8.0_45
-internal]
[Server:server-one]     at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) [rt.jar:1.8.0_4
5-internal]
[Server:server-one]     at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) [rt.jar:1
.8.0_45-internal]
[Server:server-one]     at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) [rt.jar:1.8.0_45-intern
al]
[Server:server-one]     at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418) [rt.jar:1.8.0_45-internal]
[Server:server-one]     at com.myapp.rest.logic.UserLogic.assignRollesToUser(UserLogic.java:280)

为什么我会收到java.util.ConcurrentModificationException

1 个答案:

答案 0 :(得分:0)

您必须先删除相关实体。因此,如果您有一个实体学生和每个学生都有零个或多个课程,并且学生和班级在学生课程表中多对多加入,您将为每个学生提供一系列课程,并为每个班级提供一组学生。要删除学生,必须先从多对多表中删除他们的课程。因此,如果您有一个方法RemoveStudentClass,它将学生和一个类作为参数,您可以执行类似

的操作
 public void RemoveSudent(student deleteStudent)
 {

 foreach (Class c in deleteSudent.Classes)
     {
         removeStudentClass(deleteSudent,c);
     }
         Students.Delete(deleteStudent);
 }