Home > Enterprise >  Spring Boot JPA - What's the appropriate way to assign a role to a user?
Spring Boot JPA - What's the appropriate way to assign a role to a user?

Time:04-16

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:

  1. 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'

  1. 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 role MODERATOR, the role MODERATOR 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;
  • Related