I try to remove an entity from a ManyToMany-Relation, but after saving the parent-Entity by my repository nothing is happen.
UserRepository:
public interface UserRepository extends EntityGraphJpaRepository<User, Long> {
User findByEmailIgnoreCase(String email);
Page<User> findBy(Pageable pageable);
User findByEmail(String name, com.cosium.spring.data.jpa.entity.graph.domain.EntityGraph entityGraph);
}
AbstractEntity
@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
private static final long serialVersionUID = -2523912452050488728L;
@Id
@GeneratedValue
private Long id;
@Version
private int version;
public Long getId() {
return id;
}
public int getVersion() {
return version;
}
@Override
public int hashCode() {
if (id == null) {
return super.hashCode();
}
return 31 + id.hashCode();
}
@Override
public boolean equals(Object other) {
if (id == null) {
// New entities are only equal if the instance if the same
return super.equals(other);
}
if (this == other) {
return true;
}
if (!(other instanceof AbstractEntity)) {
return false;
}
boolean test = id.equals(((AbstractEntity) other).id);
return id.equals(((AbstractEntity) other).id);
}
}
User-Entity
@NamedEntityGraphs(value = {
@NamedEntityGraph(name = "User.roles.groups", attributeNodes = {
@NamedAttributeNode("roles"),
@NamedAttributeNode("groups")
})
})
@Entity(name="userInfo")
public class User extends AbstractEntity {
@ManyToMany(mappedBy = "users", fetch = FetchType.LAZY, cascade= {CascadeType.PERSIST, CascadeType.MERGE}) //inverse side
private Set<Role> roles = new HashSet<Role>();
public void addRole(Role role) {
this.roles.add(role);
role.getUsers().add(this);
}
public void removeRole(Role role) {
this.roles.remove(role);
role.getUsers().remove(this);
}
//...
}
RoleEntity:
public class Role extends AbstractEntity {
@ManyToMany(fetch = FetchType.LAZY, cascade= {CascadeType.PERSIST,CascadeType.MERGE}) //owning side
@JoinTable(
name = "role_user",
joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"))
private Set<User> users = new HashSet<User>();
UserService:
@Service
public class UserService implements CrudEntityServiceInterface{
private UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void save(User user) {
this.userRepository.saveAndFlush(user);
}
public User updateRolesForUser(User user,Set<Role> roleItems,RoleService roleService) {
Set<Role> rolesToRemove = new HashSet<Role>();
for(Role oldRole: user.getRoles()) {
//remove roles which are deselected
if(!roleItems.contains(oldRole)) {
Optional<Role> oldRoleLoaded = (roleService.getRepository()).findById(oldRole.getId(),EntityGraphs.named("Role.users.groups"));
rolesToRemove.add(oldRoleLoaded.get());
}
}
rolesToRemove.forEach(x -> user.removeRole(x));
//added selected roles
roleItems.forEach(x -> {
Optional<Role> xLoaded = (roleService.getRepository()).findById(x.getId(),EntityGraphs.named("Role.users.groups"));
user.addRole(xLoaded.get());
} );
return user;
}
//...
}
And in my Controller I call something like
user = userService.updateRolesForUser(user,roles,roleService);
userService.save(user);
I have to use the entityGraph since I don't want to use Fetch.Eager.
Have you any clue why I can't remove an role from the user? I also tried Cascade.Remove - but this does not fix the problem and is bad practice, I guess.
EDIT: When I call
rolesToRemove.forEach(x -> {user.removeRole(x); roleService.save(x);});
the entity I want to remove from the relation is removed. But I thought everything on the user is updated by calling save on the userRepository by CascadeType.Persist? Why I have additionaly call the save for role?
Old question, but Role controls the ManyToMany relationship. If you want changes persisted to the relationship, it is the role object that must be updated, not the user side.