I’m trying to implement a User system with roles and privileges. So I have 5 tables:
- users
- roles
- privileges
- users_roles
- roles_privileges
My class user owns the relationship users_roles and the class roles owns the relationship roles_privileges.
To describe my problem lets say I have the following in my database (simplified):
- users: user
- roles: 0, 1
- privileges: a, b, c, d
- users_roles: (user, 0), (user, 1)
- roles_privileges: (0,a), (0,b), (0,c), (1,d)
Now if I retrieve the roles from the user I get the following (simplified): [0, 0, 0, 1]
On the list the role 0 appeared 3 times and the role 1 appeared 1. Which happends to correspond to the number of privileges each of the roles have.
I don’t understand why this is happening. Also this only happends when I retrive the user with the function findById from the user repository. When I retrieve the roles with the “authenticated user” this doesn’t happend.
Here’s the code of the 3 entities (simplified):
USER
@Entity @Table(name = "user")
public class User implements UserDetails {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany(fetch = FetchType.EAGER) @JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
)
private Collection<Role> roles;
@JsonIgnore public Collection<Role> getRoles() { return this.roles; } // This is the function that is returning duplicateds
}
ROLE
@Entity @Table(name = "role")
public class Role {
@JsonView(Views.Basic.class)
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany(mappedBy = "roles")
private Collection<User> users;
@ManyToMany(fetch = FetchType.EAGER) @JoinTable(
name="roles_privileges",
joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "privilege_id", referencedColumnName = "id")
)
private Collection<Privilege> privileges;
}
PRIVILEGE
@Entity @Table(name = "privilege")
public class Privilege {
@JsonView(Views.Basic.class)
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany(mappedBy = "privileges")
private Collection<Role> roles;
}
And here is the code of the endpoint that is trying to retrieve the roles from a user:
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
@ResponseBody @JsonView(Views.Basic.class)
public Response getRolesFromUser(@PathVariable Long id, @AuthenticationPrincipal final User authUser, HttpServletRequest request) {
if(!authUser.hasRole("admin")) throw (new ResponseStatusException(HttpStatus.UNAUTHORIZED, "You don't have admin privilegies."));
User user = userService.findById(id);
if(user == null) throw(new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found."));
return Response.builder().status(HttpStatus.OK).message("User's roles retrieved.").data(user.getRoles()).request(request).build();
}
user.getRoles() returns what I told earlier but authUser.getRoles() works fine. I don’t really need to resolve this problem since this is only intended to be used by the administrator to see other users roles and modify them (but I could always do that on the database). But still is a strange behaviour that I cannot understand and I would like to be able to administrate roles from the application and not from the database.
Advertisement
Answer
As a work arround, I tried to use Set instead of Collection for the user roles. It solved my problem.
This work arround, doesn’t solve the question “Why is behaving this way when using Collection?”.