Update 2020-11-21: Fixed database link problem caused by. Yml file


One, foreword

To avoid wasting time with unnecessary reading, here is a brief introduction to the project. In actual application scenarios, each user has its own role, and each role has its own roles. A user can have multiple roles, and a role can be owned by multiple users. The relationship between roles and permissions is the same. The many-to-many mapping is used to connect them and manage them.

Main functions:

  1. Add a user, role, and permission
  2. Example Delete a user, role, and permission
  3. Add a role to a user and assign permissions to the role
  4. Query the rights of a user based on the user name

Ii. Project environment

Java version: JDk1.8.0_181 IDE: IntelliJ IDEA 2019.1.3 Database: PostgresQL 9.5 Test tool: Postman

Ps: Database type does not matter, in the creation of the time to check the different database driven on the line.

3. Project file structure

When creating a project, select Spring Web Starter in Web and Spring Data JPA and PostgreSQL Driver in SQL (if mysql database is used, select mysql Driver). IDEA automatically adds dependencies to the Maven configuration file.

Here is the directory structure for this project:

Iv. Project code

Database Connection Configuration

  • application.yml
spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    username: postgres
    password: 123456
    url: jdbc:postgresql://localhost:5432/postgres
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        temp:
          use_jdbc_metadata_defaults: false
Copy the code

If you have problems connecting to the database, you can try to remove properties: and subsequent sections.

1. The Entity layer

The Entity layer is the Entity layer of the database. Generally, an Entity class corresponds to a data table in the database, and the attributes in the class correspond to the fields in the data table one by one. By default, the class name is the table name of the data table, the attribute name is the corresponding field name, and the field type corresponds to the variable type.

Simple explanation of this layer:

  1. @Entity

This annotation is used to indicate that the class is an entity class and a corresponding data table will be generated for it.

  1. @Table(name = “table_name”)

This annotation is used to modify the name of the table. The value of name is the name of the data table to be modified.

  1. @Id

This annotation is used to declare the primary key, which corresponds to the field marked above the property

  1. @GeneratedValue(strategy = GenerationType.IDENTITY)

The strategy attribute of this annotation is mainly used to set the growth mode of the primary key. IDENTITY means that the primary key is generated by the database and monotonically increases from 1.

  1. @Column(name = “column_name”)

The name attribute of this annotation is used to change the column name of the data table. Use this attribute if you don’t want to use the default

  1. @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)

This annotation is at the heart of the project. It declares a many-to-many relationship between entities, making two tables associated, usually by generating a mapping table. For the above cascade and FETCH properties, interested readers can look up the information.

  1. @JoinTable

This annotation is used in conjunction with @ManyTomany, typically on the maintenance side of many-to-many relationships, to generate the mapping table mentioned above. The name attribute indicates the name of the generated data table, the joinColumns attribute indicates the primary key of the maintenance end, and the inverseJoinColumns attribute indicates the primary key of the maintenance end. The @JoinColumn annotation is used to configure the foreign key in the mapping table. The name attribute is used to configure the name of the foreign key in the mapping table, and the referencedColumnName attribute is used to specify the field name of the foreign key in the original table.

  1. @JsonBackReference

As for this comment, I suggest removing it first and then adding it to compare the effect. It is mainly used to prevent the annotation properties from being serialized by JSON, thus avoiding the infinite loop in the many-to-many relationship query. However, with this annotation, it is no longer possible to do a reverse query (that is, you cannot query the role that has the permission using the permission name).

Note: The following code omits the package to be imported, getter and setter methods. To import related packages, use Alt+Insert. Use Alt+Insert and select Getter and Setter to quickly generate related methods.

  • User.java
@Entity
@Table(name = "user_tabel")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Integer userId;
    @Column(name = "user_name")
    private String userName;
    / / key point
    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    JoinTable(name = "user_role", name = "table name ", name =" table name ") JoinColumns = {@joinColumn (name = "user_id")}, / / inverseJoinColumns, name is relational entities Role id in the relational table name inverseJoinColumns = {@ JoinColumn (name = "role_id")})
    private List<Role> roles;
    
    // Omit the getter and setter methods
}
Copy the code
  • Role.java
@Entity
@Table(name = "role_table")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Integer roleId;
    @Column(name = "role_name")
    private String roleName;
    // As the maintained end, you only need to set the mappedBy attribute, which is the same as the name of the corresponding List variable in User
    // @jsonBackReference can prevent the properties from being serialized by JSON, resulting in an infinite loop
    @JsonBackReference
    @ManyToMany(mappedBy = "roles")
    private List<User> users;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    JoinTable(name = "roLE_auth ", name = "role_id") inverseJoinColumns =@JoinColumn(name = "auth_id") )
    private List<Authority> authorities;

	 // Omit the getter and setter methods
}
Copy the code
  • Authority.java
@Entity
@Table(name = "auth_table")
public class Authority {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "auth_id")
    private Integer authorityId;
    @Column(name = "auth_name")
    private String authorityName;

    @JsonBackReference
    @ManyToMany(mappedBy = "authorities")
    private List<Role> roles;

    // Omit the getter and setter methods
}
Copy the code

2. The dao layer

The DAO layer is the data persistence layer, also known as the Mapper layer. Mainly responsible for accessing the database, sending SQL statements to the database, and completing basic tasks of adding, deleting, checking and changing. We do this by defining an interface that inherits from the JpaRepository class. <> is filled with the name of the entity class and the variable type of the primary key of the entity.

We don’t have to implement the methods we declare in the interface, but the JpaRepository class will automatically generate the SQL statements for us as long as the naming rules are met. For details, see: Official documentation

  • UserRepository.java
public interface UserRepository extends JpaRepository<User.Integer> {
    public List<User> findAllByUserName(String userName);
    public void deleteByUserName(String userName);
}
Copy the code
  • RoleRepository.java
public interface RoleRepository extends JpaRepository<Role.Integer> {
    public List<Role> findAllByRoleName(String roleName);
    public void deleteByRoleName(String roleName);
}
Copy the code
  • AuthorityRepository.java
public interface AuthorityRepository extends JpaRepository<Authority.Integer> {
    public List<Authority> findAllByAuthorityName(String authorityName);
    public void deleteByAuthorityName(String authorityName);
}
Copy the code

3. The service layer

Service layer is the business logic layer. It mainly calls the interface of THE DAO layer and receives the data returned by the DAO layer to complete the basic function design of the project. Since the service layer of this project is added later, some functions that should be implemented in this layer are written in the Controller layer ORz.

A pit you step in:

  1. Add the @Transactional transaction annotation to ensure database consistency for update or delete operations involving more than two tables, otherwise the application will throw an exception. (The details of the business, if not familiar, are highly recommended.)
  2. If you want to delete it, you need to clear its List first, which is equivalent to clearing the relationship in the mapping table. Otherwise would be thrown org. Hibernate. Exception. ConstraintViolationException anomalies. (I used a variety of ways to clear here: if you delete the maintenance end data, just delete the maintenance end List; If you delete the data on the maintenance end, remove all roles (the maintenance end) from the List of users (maintenance end).
  • EntityService.java
@Service
public class EntityService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private AuthorityRepository authorityRepository;

    @Transactional
    public void deleteUser(String userName) {
        List<User> users = userRepository.findAllByUserName(userName);
        // If you delete the data on the maintenance end, you only clear the List on the maintenance end
        for(User user : users) {
            user.getRoles().clear();
            userRepository.save(user); // Save to the database only after executing save()
        }
        userRepository.deleteByUserName(userName);
    }

    @Transactional
    public void deleteRole(String roleName) {
        List<Role> roles = roleRepository.findAllByRoleName(roleName);
        List<User> users = userRepository.findAll();
        for (User user : users) {
            List<Role> userRole = user.getRoles();
            for (Role role : roles) {
                if (userRole.contains(role)) {
                    userRole.remove(role);
                }
                role.getAuthorities().clear();
                roleRepository.save(role);
            }
            userRepository.save(user);
        }
        roleRepository.deleteByRoleName(roleName);
    }

    @Transactional
    public void deleteAuthority(String authName) {
        List<Authority> authorities = authorityRepository.findAllByAuthorityName(authName);
        List<Role> roles = roleRepository.findAll();
        // If data on the maintenance end is to be deleted, remove all roles (on the maintenance end) from the List of users (on the maintenance end)
        for (Role role : roles) {
            List<Authority> roleAuthoritis = role.getAuthorities();
            for (Authority authority : authorities) {
                if(roleAuthoritis.contains(authority)) { roleAuthoritis.remove(authority); } } roleRepository.save(role); } authorityRepository.deleteByAuthorityName(authName); }}Copy the code

4. The controller layer

The Controller layer is the control layer, and its function is to control the request and response. It is responsible for the interaction between front and back ends, accepts the front-end request, calls the Service layer, receives the data returned by the Service layer, and finally returns the specific page and data to the client.

Simple explanation of this layer:

  1. @RestController

This is the equivalent of @controller + @responseBody. @controller injects the currently decorated class into the SpringBoot IOC container so that the class is instantiated during the run from the project where the class is located. And of course it has a semantic function, which means that this class is acting as a Controller @responseBody and what it does is it simply means that all of the data that is returned by the API in that class, regardless of whether your corresponding method returns a Map or some other Object, is returned to the client as a Json string, Depending on the attempt, if it returns a String, it is still a String.

  1. @RequestMapping(“/user”)

This annotation handles the mapping of the request address and can be used on a class or method. On a class, all methods in the class that respond to requests have that address as the parent path.

  1. @Autowired

Get in the habit of looking at the source code and clicking on the comment while holding down the Ctrl key in IDEA to see how it parsed. I understand that it’s basically instantiating the class by calling its constructor.

  1. @RequestParam(value = “userName”)

This annotation can retrieve the data in the request message (typically transmitted in key-value pairs) and then copy the obtained data into the method parameters. For example, the above example is to get a value named “userName”.

This section describes how to add users, roles, and permissions. In general, we add the permission first, then add the role, and finally add the role. For those of you wondering how to associate users with roles, roles and permissions (including yourself in the first place), there is a List object in the entity class, and the mapping relationship is created by adding the corresponding object mapping table to it. (Check out the code added below and do your own experiments to observe the phenomenon.)

As long as this isn’t your first Spring Boot program, you’ll get the hang of it. If the function is not enough, readers can also add their own.

  • EntityController
@RestController
@RequestMapping("/user")
public class EntityController {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private AuthorityRepository authorityRepository;
    @Autowired
    private EntityService entityService;

    /* Add, delete, check and change the user part */
    @RequestMapping("/finduser")
    public List<User> findByName(@RequestParam(value = "userName") String userName) {
        return userRepository.findAllByUserName(userName);
    }

    @RequestMapping("/findalluser")
    public List<User> findAllUser(a) {
        return userRepository.findAll();
    }

    @RequestMapping("/adduser")
    public List<User> addUser(@RequestParam(value = "userName") String userName,
                              @RequestParam(value = "roleName") String roleName) {
        User user = new User();
        Role role = roleRepository.findAllByRoleName(roleName).get(0);
        user.setUserName(userName);
        user.setRoles(new ArrayList<>());
        user.getRoles().add(role);// Set permissions for the user
        userRepository.save(user);
        return userRepository.findAll();
    }

    /* Add roles to users */
    @RequestMapping("/adduserrole")
    public List<User> addUserRole(@RequestParam(value = "userName") String userName,
                              @RequestParam(value = "roleName") String roleName) {
        User user = userRepository.findAllByUserName(userName).get(0);
        Role role = roleRepository.findAllByRoleName(roleName).get(0);
        if (user.getRoles() == null) {
            user.setRoles(new ArrayList<>());
        }
        user.getRoles().add(role);// Set permissions for the user
        userRepository.save(user);
        return userRepository.findAll();
    }

    @RequestMapping("/deleteuser")
    public List<User> deleteUser(
            @RequestParam(value = "userName") String userName) {
        entityService.deleteUser(userName);
        return userRepository.findAll();
    }

    /* Query user rights */
    @RequestMapping("/getauth")
    public Set<Authority> getAuthority(
            @RequestParam(value = "userName") String userName) {
        Set<Authority> authoritieSet = new HashSet<>();
        User user = userRepository.findAllByUserName(userName).get(0);
        for(Role role : user.getRoles()){
            for(Authority authority : role.getAuthorities()) { authoritieSet.add(authority); }}return authoritieSet;
    }

    /* Add, delete, check, and change roles */
    @RequestMapping("/findallrole")
    public List<Role> findAllRole(a) {
        return roleRepository.findAll();
    }

    @RequestMapping("/addrole")
    public List<Role> addRole(
            @RequestParam(value = "roleName") String roleName,
            @RequestParam(value = "authName") String authName) {
        Role role = new Role();
        Authority authority = authorityRepository.findAllByAuthorityName(authName).get(0);
        role.setRoleName(roleName);
        role.setAuthorities(new ArrayList<>());
        role.getAuthorities().add(authority);
        roleRepository.save(role);
        return roleRepository.findAll();
    }

    /* Add permission to the role */
    @RequestMapping("/addroleauth")
    public List<Role> addRoleAuth(
            @RequestParam(value = "roleName") String roleName,
            @RequestParam(value = "authName") String authName) {
        Role role = roleRepository.findAllByRoleName(roleName).get(0);
        Authority authority = authorityRepository.findAllByAuthorityName(authName).get(0);
        if (role.getAuthorities() == null) {
            role.setAuthorities(new ArrayList<>());
        }
        role.getAuthorities().add(authority);
        roleRepository.save(role);
        return roleRepository.findAll();
    }

    @RequestMapping("/deleterole")
    public List<Role> deleteRole(
            @RequestParam(value = "roleName") String roleName) {
        entityService.deleteRole(roleName);
        return roleRepository.findAll();
    }

    /* Add, delete, check, and change */
    @RequestMapping("/findallauth")
    public List<Authority> findAllAuthority(a) {
        return authorityRepository.findAll();
    }

    @RequestMapping("/addauth")
    public List<Authority> addAuthority(
            @RequestParam(value = "authName" ) String authName) {
        Authority authority = new Authority();
        authority.setAuthorityName(authName);
        authorityRepository.save(authority);
        return authorityRepository.findAll();
    }

    @RequestMapping("/deleteauth")
    public List<Authority> deletAuthority(
            @RequestParam(value = "authName") String authName) {
        entityService.deleteAuthority(authName);
        returnauthorityRepository.findAll(); }}Copy the code

Five, the operation effect

That’s a lot of functions, so let’s just pick a few.

  • The data table

After the program runs, it automatically creates five tables in the database for us, including three data tables for the entities and two mapping tables.

  • Query operation

Since some experiments have been done earlier, we already have a small amount of data in the data table, so let’s demonstrate the query now. First of all, according to the added order said above, the first permission query.

This is followed by a query for the role:

Next comes the user query:

Finally, we look up the permissions by user name.

  • Adding a Role

The operation of adding permissions is very common and not demonstrated. The operation of adding users is similar to the operation of adding roles.

6. Reference materials

  1. www.cnblogs.com/hhhshct/p/9…
  2. liuyanzhao.com/7913.html
  3. Blog.csdn.net/lidai352710…
  4. Blog.csdn.net/H_Shun/arti…
  5. Blog.csdn.net/Just_learn_…
  6. www.sojson.com/blog/295.ht…
  7. www.jianshu.com/p/6bbb5748a…