[MyBatis Plus] MyBatis Plus extension: Use the code generator to automatically generate code, the use of Db static tool classes, logical deletion, and the use of enumerations and JSON processors

Article directory

  • 1. Automatically generate code
    • 1.1 Install plug-in
    • 1.2 Generate code
  • 2. Db static tool class
    • 2.1 Understanding of Db static tool class
    • 2.2 Use cases of Db static tool class
  • 3. Logical deletion
  • 4. Enumeration processor
    • 4.1 Define enumeration constants
    • 4.2 Configure enumeration processor
    • 4.3 Test field conversion of enumeration processor
  • 5. JSON Processor
    • 5.1 Define entities
    • 5.2 Using type handlers

1. Automatically generate code

After learning the use of MyBatis Plus, we found that the basic Mapper, Service, PO and other codes are basically fixed. If this is the case, it will be very troublesome to write code repeatedly. Coincidentally, MyBatis Plus officially provides a code generator to automatically generate Mapper, Service, and PO for us based on the table structure of the database. code. However, the code generator also requires coding, which is also very troublesome. It is recommended that you use a Mybatis Plus plug-in, which can complete Mybatis Plus code generation based on the graphical interface, which is very simple.

1.1 Install plug-in

Search for “Mybatis Plus” in IDEA’s plugins and select which icon is particularly cute:


After successful installation, you can see an Orther option in the navigation bar of IDEA:

This includes database configuration and code generation options.

1.2 Generate code

At this time, we happen to have a address table that has not yet written the corresponding code. At this time, we can use this plug-in to automatically generate the code related to the address table.

  1. First configure the database and fill in the basic information of the database connection in the pop-up window:

  1. Then click other in the IDEA navigation bar again and select Generate Code:

Click “code generatro” and the code will be automatically generated to the specified location.

2. Db static tool class

2.1 Understanding of Db static tool classes

Sometimes different Service classes call each other. In order to avoid circular dependency problems, Mybatis Plus provides a static tool class: Db, some of which static methods are similar to IServiceThe method signatures are basically the same and can also help us implement CRUD functions:

For example, the following use case:

/**
 * Get user information with id 1
 */
@Test
void testDbGet() {<!-- -->
    User user = Db.getById(1L, User.class);
    System.out.println(user);
}

/**
 * Query user information with "o" in the user name and balance >= 1000
 */
@Test
void testDbList() {<!-- -->
    List<User> list = Db.lambdaQuery(User.class)
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000).list();
    System.out.println(list);
}

/**
 * Set the balance of the user named Rose to 2500
 */
@Test
void testDbUpdate() {<!-- -->
    Db.lambdaUpdate(User.class)
            .set(User::getBalance, 2500)
            .eq(User::getUsername, "Rose").update();
}

It can be found that the use of the static class corresponding to Db is similar to the previous use method.

2.2 Use cases of Db static tool class

Example 1: Modify the interface for user query based on id, and return the user’s delivery address list while querying the user.

  1. First, we need to add a VO object of the shipping address:
@Data
@ApiModel(description = "Shipping address VO")
public class AddressVO{<!-- -->

    @ApiModelProperty("id")
    private Long id;

    @ApiModelProperty("User ID")
    private Long userId;

    @ApiModelProperty("province")
    private String province;

    @ApiModelProperty("city")
    private String city;

    @ApiModelProperty("County/District")
    private String town;

    @ApiModelProperty("mobile phone")
    private String mobile;

    @ApiModelProperty("Detailed address")
    private String street;

    @ApiModelProperty("Contact")
    private String contact;

    @ApiModelProperty("Is it the default 1 default 0 no")
    private Boolean isDefault;

    @ApiModelProperty("Remarks")
    private String notes;
}
  1. Then, transform the original UserVO class and add an address attribute at the end:

  1. Next, modify the business interface for querying users based on id in UserController:
@GetMapping("/{id}")
@ApiOperation("Query user interface based on id")
public UserVO queryUserById(@PathVariable("id") Long id) {<!-- -->
    return userService.queryUserAndAddressById(id);
}

At this time, a new queryUserAndAddressById method has been added.

  1. Implement the queryUserAndAddressById method in the service layer

    • First define the method in IUserService:
    public interface IUserService extends IService<User> {<!-- -->
        UserVO queryUserAndAddressById(Long id);
    }
    
    • Then, implement this method in UserServiceImpl:
    @Override
    public UserVO queryUserAndAddressById(Long id) {<!-- -->
        // 1. Query user
        User user = getById(id);
    
        // 2. Use Db to query address categories based on users
        List<Address> addresses = Db.lambdaQuery(Address.class)
                .eq(Address::getUserId, user.getId())
                .list();
    
        // 3. Process VO
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
    
        return userVO;
    }
    

When querying the address, the static method in Db is used, thus avoiding the injection of AddressService and reducing the risk of circular dependencies.

After completing the above code, the user information queried through id will have address information:

Example 2: Modify the interface for querying users in batches based on ID, requiring all addresses corresponding to the user to be queried

  1. Modify the controller interface:
@GetMapping
@ApiOperation("Batch query user interface based on id")
public List<UserVO> queryUserByIds(@ApiParam("User ID Collection") @RequestParam("ids") List<Long> ids) {<!-- -->
    return userService.queryUserAndAddressByIds(ids);
}
  1. Implement the queryUserAndAddressByIds method in the service layer

    • First define the method in IUserService:
    public interface IUserService extends IService<User> {<!-- -->
        List<UserVO> queryUserAndAddressByIds(Long id);
    }
    
    • Then, implement this method in UserServiceImpl:
    @Override
    public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {<!-- -->
        // 1. Query user collection
        List<User> users = this.listByIds(ids);
        if (users.isEmpty()) {<!-- -->
            return Collections.emptyList();
        }
    
        // 2. Query address
        // 2.1 Get user id
        List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        // 2.2 Query address
        List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
        // 2.3 Address converted to VO
        List<AddressVO> addressVOS = BeanUtil.copyToList(addresses, AddressVO.class);
        // 2.4 Group addresses VO according to userId
        Map<Long, List<AddressVO>> addressesMap = new HashMap<>();
        if (CollUtil.isNotEmpty(addresses)) {<!-- -->
            addressesMap = addressVOS.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        }
        // 3. Convert VO and return
        List<UserVO> list = new ArrayList<>(users.size());
        for (User user : users) {<!-- -->
            UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
            // Fill in the address
            userVO.setAddresses(addressesMap.get(user.getId()));
            list.add(userVO);
        }
        return list;
    }
    

Note:

  1. When using the queried user ID to query address information, avoid querying the database in a loop. Therefore, the user ID collection is first obtained, and then the address information is queried in batches based on these ID collections.

  2. Classify the queried address information according to user ID, and then set it into the corresponding UserVO object.

3. Logical deletion

For some more important data, we often use logical deletion solutions, such as:

  • Add a field to the table to mark whether data has been deleted

  • Set flag to true when deleting data

  • Filter out data marked true when querying

Once logical deletion is adopted, all query and deletion logic will change accordingly, and the corresponding database operations will become very troublesome. Fortunately, to solve this trouble, MyBatis Plus provides support for logical deletion.

Note that only the SQL statements generated by Mybatis Plus support automatic logical deletion. Custom SQL still requires manual processing of logical deletion.

The following demonstrates using the logical deletion function of MyBatis Plus:

  1. Add a tombstone field to the address table:
alter table address add deleted bit default b'0' null comment 'logical delete';
  1. Then add the deleted field to the Address entity:
  2. Next, configure the tombstone field in application.yml
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # Logical delete field
      logic-delete-value: 1 # When the value of deleted is 1, it is logically deleted.
      logic-not-delete-value: 0 # When the value of deleted is 0, there is no logical deletion

After completing all the above preparations, we can execute a deleted test method:

@Test
void testLogicDelete() {<!-- -->
    addressService.removeById(59L);
}

Run this code:

It was found that the deleted field of the address information with ID 59 was set to 1. If you query this data again at this time:


It is found that this data cannot be queried at this time, but it still exists in the database.

Therefore, after turning on the logical deletion function, we can do CRUD like normal deletion, basically without having to consider code logic issues. Still very convenient. However, there are certain problems with using logical deletion, such as:

  • This will lead to more and more junk data in the database table, thus affecting query efficiency;
  • All SQL requires judgment on tombstone fields, which affects query efficiency.

Therefore, it is not recommended to use the logical deletion function. If the data cannot be deleted, you can migrate the data to other tables.

4. Enumeration processor

There is a user status field in the User entity class:


We usually define an enumeration for fields like this, and when making business judgments, we can directly make comparisons based on the enumeration. However, our database uses the int type, and the corresponding PO is also Integer. Therefore, you must manually convert enumerations and Integer during business operations, which is very troublesome. Therefore, Mybatis Plus provides a type converter for processing enumerations, which can help us automatically convert enumeration types and database types.

4.1 Define enumeration constants

First, we define an enumeration constant for this status field in the user table:

@Getter
public enum UserStatus {<!-- -->

    NORMAL(1, "normal"),
    FROZE(2, "Freeze"),
    ;

    private final int value;
    private final String desc;

    UserStatus(int value, String desc) {<!-- -->
        this.value = value;
        this.desc = desc;
    }
}

Then change the status field in the User class to the UserStatus type:

To let Mybatis Plus handle automatic conversion between enumeration and database types, we must tell Mybatis Plus which field value in the enumeration is used as the database value. Mybatis Plus provides the @EnumValue annotation to mark enumeration properties:

4.2 Configuring the enumeration processor

Add the following configuration in the application.yml file to enable the function of enumerating processors:

mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler # Enumeration processor

4.3 Test field conversion of enumeration processor

For example, query a user based on id:
At this time, the status of the User class is queried The fields will be of enumeration type.

At the same time, in order to make the page query results also in enumeration format, we need to modify the status attribute in UserVO:

And, pass @JsonValue in the UserStatus enumeration The field displayed when the annotation tag JSON is serialized is desc:


Finally, query on the page and the results are as follows:

5. JSON Processor

There is a info field in the user table of the database, which is of JSON type:

The format is like this:

{<!-- -->"age": 20, "intro": "Buddhist youth", "gender": "male"}

But currently the User entity class is of String type, because there is no such type as JSON in Java:

As a result, it is very inconvenient for us to read the attributes in info. If you want to easily obtain it, the type of info is best to be a Map or entity class.

Once we change info to an object type, we need to manually convert it to String when writing to the database, and then manually convert it to an object when reading the database, which will be very troublesome.

Therefore, Mybatis Plus provides many type processors for special types of fields to solve the problem of conversion between special field types and database types. For example, when processing JSON, you can use the JacksonTypeHandler processor.

Next, let’s take a look at how to use this processor.

5.1 Define entities

First, we define a separate entity class to match the attributes of the info field:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {<!-- -->
    private Integer age;
    private String intro;
    private String gender;
}

5.2 Using type processors

Next, modify the info field of the User class to the UserInfo type and declare the type processor:

Note that autoResultMap needs to be set to true for it to take effect.

The test can find that all data is correctly encapsulated into UserInfo :


At the same time, in order to return the results returned by the page in object format, we need to modify the type of the info field in UserVO:

At this time, the query results on the page are as follows:


It is found that the info displayed at this time is in JSON format.