I'm trying to develop an application in which users get assigned a specific role, and based on that role they have access to certain functionalities. This is what I have so far:
User class:
@Entity
@Table(name = "users",
uniqueConstraints = {
@UniqueConstraint(columnNames = "username"),
@UniqueConstraint(columnNames = "email")
})
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "usid_generator")
@SequenceGenerator(name = "usid_generator", initialValue = 1000, allocationSize = 1, sequenceName = "usid_seq")
private Long id;
@NotBlank
@Size(max = 20)
private String username;
@NotBlank
@Size(max = 50)
@Email
private String email;
@NotBlank
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Size(max = 120)
private String password;
@OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY)
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
private Integer notifications;
Role class:
@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "roleid_generator")
@SequenceGenerator(name = "roleid_generator", initialValue = 100, allocationSize = 1, sequenceName = "roleid_seq")
private Integer id;
@Enumerated(EnumType.STRING)
@Column(length = 20)
private ERole name;
However, I'm runnning into some issues. I have some buttons on the front end of my application that allow me to change the role of a user, here is one example:
@PutMapping("/users/setadmin/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<User> setAdmin(@PathVariable("id") Long id, @RequestBody User user) {
Optional<User> userData = userRepository.findById(id);
Optional<Role> adminRole = roleRepository.findByName(ERole.ROLE_ADMIN);
Set<Role> roles = new HashSet<>();
roles.add(adminRole.get());
if (userData.isPresent()) {
User _user = userData.get();
_user.setRoles(roles);
return new ResponseEntity<>(userRepository.save(_user), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
The ERoles are stored in a separate class:
public enum ERole {
ROLE_USER,
ROLE_MODERATOR,
ROLE_ADMIN
}
However, by using this approach I'm getting two errors:
- I can't seem to have more than three users in the whole database. Every time I create a new user with a role that another user already has I get an error. Here is an example of what I got when trying to assign the role
USER
to a new account, after already having another user with that role in the DB:
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'user_roles.UK_5q4rc4fh1on6567qk69uesvyf'
- When I delete a user from the DB, the role that they had assigned gets deleted from the
roles
table. So, if I delete a user who had the roleMODERATOR
, the roleMODERATOR
itself gets deleted as well.
I'm guessing it's an issue with the CascadeType.ALL
property on the User class. I was reading some documentation about the types of cascades, but I can't figure out which one I should use, or if that's the problem at all. Any help is much appreciated
CodePudding user response:
@OneToMany annotation indicates that one user can have multiple roles, but one role cannot relate to multiple users.
Try with this:
User:
@ManyToMany(cascade = CascadeType.ALL)
private Set<Role> roles = new HashSet<>();
Role:
@ManyToMany(mappedBy = "roles")
private Set<User> users;