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?”.