MyBatis Plus usage documentation instructions

MyBatis Plus Basics

1. Introduction

MyBatis is a semi-automatic ORM framework.

MyBatis-Plus (referred to as MP) is a MyBatis The enhancement tool, based on MyBatis, only enhances and does not change, and is born to simplify development and improve efficiency.

[Tips] The course is based on the latest version 3.1.1.

[External link image transfer failed, the source site may have an anti-leech mechanism, it is recommended to save the image and upload it directly (img-kWZr0KOG-1614770539124)(assets/1557127576570.png)]

Has powerful features

1) Non-invasive: only make enhancements without changing, introducing it will not affect existing projects, it is as smooth as silk

2) Low loss: Basic CURD will be automatically injected at startup, performance is basically lossless, and direct object-oriented operation

3) Powerful CRUD operations: Built-in general Mapper and general Service, most CRUD operations on a single table can be implemented with only a small amount of configuration, and more powerful conditional constructors to meet various usage needs

4) Support Lambda form invocation: Through Lambda expressions, various query conditions can be easily written, and there is no need to worry about writing wrong fields.

5) Support multiple databases: support MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer2005, SQLServer and other databases

6) Support the automatic generation of primary keys: support up to 4 primary key strategies (including a distributed unique ID generator - Sequence), which can be freely configured to perfectly solve the primary key problem

7) Support XML hot loading: The XML corresponding to Mapper supports hot loading. For simple CRUD operations, it can even be started without XML, but the latest version has been removed

8) Support ActiveRecord mode: Support ActiveRecord form invocation, and entity classes can perform powerful CRUD operations only by inheriting the Model class. ActiveRecord is primarily a data access design pattern that enables the mapping of data objects to relational databases.

9) Support custom global general operations: support global general method injection Write once, use anywhere

10) Support automatic escaping of keywords: support automatic escaping of database keywords (order, key...), and custom keywords

11) Built-in code generator: using code or Maven plug-in can quickly generate Mapper , Model , Service , Controller layer code, support template engine, and more custom configurations waiting for you to use

12) Built-in paging plug-in: Based on MyBatis physical paging, developers do not need to care about specific operations. After configuring the plug-in, writing paging is equivalent to ordinary List query

13) Built-in performance analysis plug-in: It can output Sql statements and their execution time. It is recommended to enable this function during development and testing, which can quickly identify slow queries

14) Built-in global interception plug-in: provides intelligent analysis and blocking of delete and update operations on the entire table, and can also customize interception rules to prevent misoperation

15) Built-in Sql injection stripper: supports Sql injection stripping, effectively preventing Sql injection attacks

2. Frame structure

[External link image transfer failed, the source site may have anti-leech mechanism, it is recommended to save the image and upload it directly (img-A4o16Ixu-1614770539126)(assets/architecture 02.png)]

3. Basic development environment

  1. Based on Java development, jdk 1.8+ version is recommended.
  2. Need to use Spring Boot
  3. Need to use Maven
  4. Requires MySQL

1. Prepare the data

We need to prepare a user table

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
	id BIGINT(20) NOT NULL COMMENT 'primary key ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT 'Name',
	age INT(11) NULL DEFAULT NULL COMMENT 'age',
	email VARCHAR(50) NULL DEFAULT NULL COMMENT 'Mail',
	PRIMARY KEY (id)
);

Then add some data to the user table

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Cuihua', 18, 'cuihua@kkb.com'),
(2, 'spring flowers', 20, 'chunhua@kkb.com'),
(3, 'Huahua', 28, 'huahua@kkb.com'),
(4, 'Cuicui', 21, 'cuicui@kkb.com'),
(5, 'Chunchun', 24, 'chunchun@kkb.com');

2. Hello World

Our Goal: In a few simple steps, we implemented the CRUD functionality of the User table without even writing an XML file!

Step 1: Create a Spring Boot project

​ Recommended, log in to https://start.spring.io to build and download a clean Spring Boot project.

Step 2: Edit the pom.xml file to add related dependencies

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
</parent>

<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.8</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.1.1</version>
    </dependency>    
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

[Note] After the introduction of MyBatis-Plus, please do not introduce MyBatis and MyBatis-Spring again to avoid problems caused by version differences.

[If it is a SpringMVC project]

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>3.1.1</version>
</dependency>

Step 3: Configure the application.yml file

Edit the application.yml configuration file, mainly to add the relevant configuration of the druid database:

# DataSource Config
spring:
  datasource:
    # Use druid data source here
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/aaa?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 1234
    filters: stat
    maxActive: 20
    initialSize: 1
    maxWait: 60000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: select 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxOpenPreparedStatements: 20

Add the @MapperScan annotation to the Spring Boot startup class to scan the Mapper folder:

@SpringBootApplication
@MapperScan("com.kkb.mapper")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(QuickStartApplication.class, args);
    }

}

[Notice] If it is a SpringMVC project, you need to configure MapperScan in the <bean> tag.

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.kkb.mapper"/>
</bean>

Then you can also adjust the SqlSessionFactory to Mybatis-Plus's SqlSessionFactory.

<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>

Step 4: Create the corresponding class

Create a new User class:

@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

[Note] @Data is an annotation provided by Lombok that can make the code more concise and can reduce a lot of template code. The annotation is on the class, providing read and write properties for the class, and also provides equals(), hashCode(), toString() methods.

If it is used in Eclipse, we need to install it manually, please refer to https://www.projectlombok.org/setup/eclipse for details

[Reference: Lombok Common Notes]

@NonNull : Annotation on the parameter, If the class parameter is null , exception will be reported,  throw new NullPointException(parameter name)
@Cleanup : comment before the referenced variable, Automatically recycle resources Default call close() method
@Getter/@Setter : Annotation on the class, Provide read-write properties for classes
@Getter(lazy=true) :
@ToString : Annotation on the class, Provided for the class toString() method
@EqualsAndHashCode : Annotation on the class, Provided for the class equals() and hashCode() method
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor : Annotation on the class, Provide no arguments for the class,There are required parameters specified, full parameter constructor
@Data : Annotation on the class, Provide read-write properties for classes, Also provides equals(),hashCode(),toString() method
@Value :
@Builder : Annotation on the class, Provide a class with an internal Builder
@SneakThrows :
@Synchronized : Annotation on the method, Provide synchronization lock for method
@Log :
@Log4j : Annotation on the class, Give the class a property named log of log4j the log object
@Slf4j : Annotation on the class, Give the class a property named log of log4j the log object

Create a new UserMapper mapping interface class:

public interface UserMapper extends BaseMapper<User> {
}

Step 5: Have fun testing

Create a new KKBTest test class

@RunWith(SpringRunner.class)
@SpringBootTest
public class KKBTest {

    @Autowired
    private UserMapper userMapper;

@Test
	public void testSelect(){
		
		// Here null means that there is no need to query based on parameters
		// There are many ways to call CRUD related

		// 1. Query all data
		List<User> userList = userMapper.selectList(null);
		userList.forEach(user -> System.out.println(user.getName()));
		
		// 2. Delete by id
		userMapper.deleteById(1);
		
		// 3. Add data
		User user = new User();
		user.setName("Lao Wang");
		user.setEmail("laowang@kkb.com");
		user.setAge(18);
		userMapper.insert(user);
		
		// 4. Update data
		user.setName("old king");
		user.setEmail("laowangwang@kkb.com");
		userMapper.updateById(user);
	}
    
}

4. Common Notes

  1. @TableName: table name description
  2. @TableId: primary key annotation
  3. @TableField: field annotation (non-primary key)
  4. @Version: optimistic locking annotation, mainly used to mark fields
  5. @EnumValue: Annotated by the enumeration class (annotated on the enumeration field)
  6. @TableLogic: table field logic processing annotation (logical deletion)
  7. @SqlParser: tenant annotation (support annotation on mapper)
  8. @KeySequence: sequence primary key strategy, attributes are: value, resultMap

Case: Multi-table join query

1. Prepare the data

User user table (as before)
CREATE TABLE USER
(
	id BIGINT(20) NOT NULL COMMENT 'primary key ID',
	NAME VARCHAR(30) NULL DEFAULT NULL COMMENT 'Name',
	age INT(11) NULL DEFAULT NULL COMMENT 'age',
	email VARCHAR(50) NULL DEFAULT NULL COMMENT 'Mail',
	PRIMARY KEY (id)
);

INSERT INTO USER (id, NAME, age, email) VALUES
(1, 'cuihua', 18, 'test1@baomidou.com'),
(2, 'huahua', 20, 'test2@baomidou.com'),
(3, 'cunhua', 28, 'test3@baomidou.com'),
(4, 'laowang', 21, 'test4@baomidou.com'),
(5, 'xiaomei', 24, 'test5@baomidou.com');
Role role table
CREATE TABLE role (
	id BIGINT(20) NOT NULL COMMENT 'primary key ID',
	rolecode VARCHAR(100) NULL DEFAULT NULL COMMENT 'role number',
	rolename VARCHAR(100) NULL DEFAULT NULL COMMENT 'character name',
	PRIMARY KEY (id)
 );

 INSERT INTO role(id, rolecode, rolename) VALUES 
 (1, '001', 'President'),
 (2, '002', 'Dean'),
 (3, '003', 'group leader');
Permission permission table
CREATE TABLE permission (
	id BIGINT(20) NOT NULL COMMENT 'primary key ID',
	permissioncode VARCHAR(100) NULL DEFAULT NULL COMMENT 'permission number',
	permissionname VARCHAR(100) NULL DEFAULT NULL COMMENT 'permission name',
	path VARCHAR(100) NULL DEFAULT NULL COMMENT 'map path',
	PRIMARY KEY (id)
 );

INSERT INTO permission(id, permissioncode, permissionname) VALUES 
 (1, '111', 'mess around'),
 (2, '222', 'mess up occasionally'),
 (3, '333', 'little mess');
UserRole user role association table
CREATE TABLE userrole (
	id BIGINT(20) NOT NULL COMMENT 'primary key ID',
	username VARCHAR(100) NULL DEFAULT NULL COMMENT 'username',
	rolecode VARCHAR(100) NULL DEFAULT NULL COMMENT 'role number',
	PRIMARY KEY (id)
 );

 INSERT INTO userrole(id, username, rolecode) VALUES 
 (1, 'cuihua', '001'),
 (2, 'chunhua', '003'),
 (3, 'huahua', '002');
RolePermission role permission association table
CREATE TABLE rolepermission (
	id BIGINT(20) NOT NULL COMMENT 'primary key ID',
	rolecode VARCHAR(100) NULL DEFAULT NULL COMMENT 'role number',
	permissioncode VARCHAR(100) NULL DEFAULT NULL COMMENT 'permission number',
	PRIMARY KEY (id)
 );

 INSERT INTO rolepermission(id, rolecode, permissioncode) VALUES 
 (1, '001', '111'),
 (2, '002', '222'),
 (3, '003', '333');

2. Create entity class

File path: com.kkb.pojo

User class
@Data
@TableName("user")
@ApiModel("User class")
public class User implements Serializable {

    @ApiModelProperty(name = "id", value = "ID primary key")
    @TableId(type = IdType.AUTO)
    private String id;

    @ApiModelProperty(name = "name", value = "username")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String name;

    @ApiModelProperty(name = "age", value = "age")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer age;

    @ApiModelProperty(name = "email", value = "Mail")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String email;

    @TableField(exist = false)
    private Set<Role> roles = new HashSet<>();

}

[Simple understanding] The @ApiModel and @ApiModelProperty annotations in the code come from Swagger This API simplifies the development of open source frameworks, and interested students can learn about it.

For use in Springboot, its dependencies need to be introduced in the pom.xml file.

<dependency>
    <groupId>com.spring4all</groupId>
    <artifactId>swagger-spring-boot-starter</artifactId>
    <version>1.7.0.RELEASE</version>
</dependency>

In addition, you can also introduce its dependencies separately, and you can specify the corresponding or latest version yourself.

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>lastest version</version>
</dependency>

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>lastest version</version>
</dependency>
Role class
@Data
@TableName("role")
@ApiModel("role class")
public class Role implements Serializable {

    @ApiModelProperty(name = "id", value = "ID primary key")
    @TableId(type = IdType.AUTO)
    private String id;

    @ApiModelProperty(name = "roleCode", value = "role number")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String roleCode;

    @ApiModelProperty(name = "roleName", value = "character name")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String roleName;

    @TableField(exist = false)
    private Set<Permission> permissions = new HashSet<>();

}
Permission class
@Data
@TableName("permission")
@ApiModel("permission class")
public class Permission implements Serializable {

    @ApiModelProperty(name = "id", value = "ID primary key")
    @TableId(type = IdType.AUTO)
    private String id;

    @ApiModelProperty(name = "permissionCode", value = "permission number")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String permissionCode;

    @ApiModelProperty(name = "permissionName", value = "permission name")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String permissionName;

    @ApiModelProperty(name = "path", value = "map path")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String path;

}
UserRole class
@Data
@TableName("userRole")
@ApiModel("User role relationship class")
public class UserRole implements Serializable{

    @ApiModelProperty(name = "id", value = "ID primary key")
    @TableId(type = IdType.AUTO)
    private String id;

    @ApiModelProperty(name = "username", value = "username")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String username;

    @ApiModelProperty(name = "roleCode", value = "role number")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer roleCode;

}
RolePermission class
@Data
@TableName("rolePermission")
@ApiModel("Role permission relationship class")
public class RolePermission implements Serializable{

    @ApiModelProperty(name = "id", value = "ID primary key")
    @TableId(type = IdType.AUTO)
    private String id;

    @ApiModelProperty(name = "roleCode", value = "role number")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer roleCode;

    @ApiModelProperty(name = "permission", value = "permission number")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String permissionCode;

}

3. Create the Mapper interface class

File path: com.kkb.mapper

UserMapper interface class
public interface UserMapper extends BaseMapper<User> {
	
    @Select("select * from user where name = #{name}")
    public User findUserByName(String username);

    // Get the roles a user has
    @Select("select * from  role where rolecode in(select rolecode from userrole where username = #{userName})")
    public Set<Role> getUserRoles(String username);

}
RoleMapper interface class
public interface RoleMapper extends BaseMapper<User> {

    // Get the permissions of the role
    @Select("select * from permission where permissioncode in (select permissioncode from rolepermission where rolecode = #{roleCode})")
    public Set<Permission> getRolePermissions(String roleCode);

}

4. Scan mapper in startup class

@SpringBootApplication
@MapperScan("com.kkb.mapper")
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

5. Edit the application.yml file

mybatis-plus:
   type-aliases-package: com.kkb.pojo
   configuration:
    map-underscore-to-camel-case: true  

6. Have fun testing together

@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo {
	
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Test
    public void testUserRole() {

        User user = userMapper.findUserByName("cuihua");
        Set<Role> roles = userMapper.getUserRoles("cuihua");

        for (Role role : roles) {
                role.setPermissions(roleMapper.
				getRolePermissions(role.getRoleCode()));
            }

        user.setRoles(roles);
        System.out.println(user);
    }
}

MyBatis Plus Core Functions

1. Code generator

AutoGenerator is the code generator of MyBatis-Plus. Through AutoGenerator, the codes of Entity, Mapper, Mapper XML, Service, Controller and other modules can be quickly generated, which greatly improves the development efficiency.

// Demonstration example, execute the main method console, enter the module table name and press Enter to automatically generate the corresponding project directory
public class CodeGenerator {

    /**
     * <p>
     * read console content
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("please enter" + tip + ": ");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("Please enter the correct" + tip + "!");
    }

    public static void main(String[] args) {
        // Code generator
        AutoGenerator mpg = new AutoGenerator();

        // Global configuration
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("jobob");
        gc.setOpen(false);
        // gc.setSwagger2(true); entity attribute Swagger2 annotation
        mpg.setGlobalConfig(gc);

        // Data source configuration
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("password");
        mpg.setDataSource(dsc);

        // package configuration
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("module name"));
        pc.setParent("com.baomidou.ant");
        mpg.setPackageInfo(pc);

        // custom configuration
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // If the template engine is freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // If the template engine is velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // Custom output configuration
        List<FileOutConfig> focList = new ArrayList<>();
        // Custom configuration will be output first
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // Customize the output file name, if your Entity has set the prefix and suffix, note that the name of the xml will change accordingly! !
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // Determine whether a custom folder needs to be created
                checkDir("The directory created by calling the default method");
                return false;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // configuration template
        TemplateConfig templateConfig = new TemplateConfig();

        // Configure custom output templates
        //Specify a custom template path, be careful not to bring .ftl/.vm, it will be automatically recognized according to the template engine used
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // Policy configuration
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setSuperEntityClass("com.baomidou.ant.common.BaseEntity");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setSuperControllerClass("com.baomidou.ant.common.BaseController");
        strategy.setInclude(scanner("Table name, separated by multiple commas").split(","));
        strategy.setSuperEntityColumns("id");
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}

1. Specific use

1) Add dependencies

MyBatis-Plus has removed the default dependencies of the code generator and template engine since 3.0.3, and you need to manually add related dependencies:

  • Add code generator dependencies

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.1.1</version>
    </dependency>
    
  • Add template engine dependency, MyBatis-Plus supports Velocity (default), Freemarker, Beetl, users can choose the template engine they are familiar with, if they are not comfortable, they can also use a custom template engine.

    Velocity (default):

    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.1</version>
    </dependency>
    

    Freemarker:

    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.28</version>
    </dependency>
    

    Beetl:

    <dependency>
        <groupId>com.ibeetl</groupId>
        <artifactId>beetl</artifactId>
        <version>3.0.0.M1</version>
    </dependency>
    

    [Note]If you choose a non-default engine, you need to set the template engine in AutoGenerator.

    AutoGenerator generator = new AutoGenerator();
    
    // set freemarker engine
    generator.setTemplateEngine(new FreemarkerTemplateEngine());
    
    // set beetl engine
    generator.setTemplateEngine(new BeetlTemplateEngine());
    
    // set custom engine (reference class is your custom engine class)
    generator.setTemplateEngine(new CustomTemplateEngine());
    
    // other config
    ...
    

2) Write the configuration

The code generator of MyBatis-Plus provides a large number of custom parameters for users to choose, which can meet the needs of most people.

  • Configure GlobalConfig

    GlobalConfig globalConfig = new GlobalConfig();
    globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
    globalConfig.setAuthor("jobob");
    globalConfig.setOpen(false);
    
  • Configure DataSourceConfig

    DataSourceConfig dataSourceConfig = new DataSourceConfig();
    dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
    dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
    dataSourceConfig.setUsername("root");
    dataSourceConfig.setPassword("password");
    
  • Configure DataSourceConfig

    DataSourceConfig dataSourceConfig = new DataSourceConfig();
    dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
    dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
    dataSourceConfig.setUsername("root");
    dataSourceConfig.setPassword("password");
    

2. Custom template engine

Please inherit the class com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine

3. Custom code templates

// Specify the custom template path, location: /resources/templates/entity2.java.ftl (or .vm)
// Be careful not to bring .ftl (or .vm), it will be automatically recognized according to the template engine used
TemplateConfig templateConfig = new TemplateConfig()
    .setEntity("templates/entity2.java");

AutoGenerator mpg = new AutoGenerator();
// Configure custom templates
mpg.setTemplate(templateConfig);

4. Custom property injection

InjectionConfig injectionConfig = new InjectionConfig() {
    //Custom property injection: abc
    //In the .ftl (or .vm) template, get properties through ${cfg.abc}
    @Override
    public void initMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
        this.setMap(map);
    }
};
AutoGenerator mpg = new AutoGenerator();
//Configure custom property injection
mpg.setCfg(injectionConfig);
entity2.java.ftl
 Custom property injection abc=${cfg.abc}

entity2.java.vm
 Custom property injection abc=$!{cfg.abc}

2. CRUD interface

1. Mapper CRUD interface

[Simple Description]

  • Generic CRUD wrapper BaseMapper Interface, for Mybatis-Plus to automatically resolve entity table relationship mapping when it starts, convert it to Mybatis internal object injection container
  • The generic T is any entity object
  • The parameter Serializable is any type of primary key. Mybatis-Plus does not recommend the use of composite primary keys. Each table has its own unique id primary key.
  • Object Wrapper is Conditional constructor

insert

/**
 * insert a record
 *
 * @param entity entity object
 * @return Number of records successfully inserted
 */
int insert(T entity);

deleteById

/**
 * delete by ID
 *
 * @param id primary key ID
 * @return Number of successful records deleted
 */
int deleteById(Serializable id);

deleteByMap

/**
 * Delete records based on columnMap conditions
 *
 * @param columnMap table field map object
 * @return Number of successful records deleted
 */
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

delete

/**
 * Delete records based on entity condition
 *
 * @param wrapper The entity object encapsulates the operation class (may be null)
 * @return Number of successful records deleted
 */
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);

deleteBatchIds

/**
 * Delete (bulk delete by ID)
 *
 * @param idList List of primary key ID s (cannot be null and empty)
 * @return Number of successful records deleted
 */
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

updateById

/**
 * Edit by ID
 *
 * @param entity entity object
 * @return Modify the number of successful records
 */
int updateById(@Param(Constants.ENTITY) T entity);

update

/**
 * Update records based on whereEntity condition
 *
 * @param entity        entity object (set conditional value, nullable)
 * @param updateWrapper The entity object encapsulates the operation class (can be null, the entity inside is used to generate the where statement)
 * @return Modify the number of successful records
 */
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

selectById

/**
 * Query by ID
 *
 * @param id primary key ID
 * @return entity
 */
T selectById(Serializable id);

selectBatchIds

/**
 * Query (Batch query based on ID)
 *
 * @param idList List of primary key ID s (cannot be null and empty)
 * @return entity collection
 */
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

selectByMap

/**
 * Query (according to columnMap condition)
 *
 * @param columnMap table field map object
 * @return entity collection
 */
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

selectOne

/**
 * Query a record based on entity condition
 *
 * @param queryWrapper entity object
 * @return entity
 */
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectCount

/**
 * According to the Wrapper condition, query the total number of records
 *
 * @param queryWrapper entity object
 * @return Number of records that meet the condition
 */
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectList

/**
 * Query all records according to entity condition
 *
 * @param queryWrapper The entity object encapsulates the operation class (may be null)
 * @return entity collection
 */
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectMaps

/**
 * Query all records according to Wrapper conditions
 *
 * @param queryWrapper The entity object encapsulates the operation class (may be null)
 * @return Field mapping object Map collection
 */
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectObjs

/**
 * Query all records according to Wrapper conditions
 * Note: Only the value of the first field is returned
 *
 * @param queryWrapper The entity object encapsulates the operation class (may be null)
 * @return A collection of field mapping objects
 */
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectPage

/**
 * According to the entity condition, query all records (and turn pages)
 *
 * @param page         Paging query condition (can be RowBounds.DEFAULT)
 * @param queryWrapper The entity object encapsulates the operation class (may be null)
 * @return Entity Paging Object
 */
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectMapsPage

/**
 * According to Wrapper conditions, query all records (and turn pages)
 *
 * @param page         Paging query conditions
 * @param queryWrapper Entity object encapsulation operation class
 * @return Field mapping object Map paging object
 */
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

[Simple example]

@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo {
	
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect(){

        // 1. Add data (be sure to ensure that the primary key of the table is self-incrementing)
        /*User user = new User();
        user.setName("Pharaoh");
        user.setEmail("laowang@kkb.com");
        user.setAge(18);
        userMapper.insert(user);*/

        // 2. Delete by id
        // userMapper.deleteById(6);

        // 3. According to the columnMap condition, delete the record
        // Maps belongs to the com.google.common.collect.Maps package from Guava
        // Guava means pomegranate in Chinese. This project is an open source project of Google and contains many common Java libraries that are core to Google.
        /*Map<String, Object> columnMap = Maps.newHashMap();
        columnMap.put("id", 3);

        userMapper.deleteByMap(columnMap);*/

        // 4. Query all data
        // Without specifying conditions, pass null directly
        /*List<User> userList = userMapper.selectList(null);
        userList.forEach(user -> System.out.println("User: " + user));*/

        // 5. Batch query
        List<String> idList = new ArrayList<String>();
        idList.add("1");
        idList.add("2");
        List<User> userList = userMapper.selectBatchIds(idList);
        userList.forEach(user -> System.out.println("user:" + user));

    }
}

2. Service CRUD interface

[Simple Description]

  • Generic Service CRUD wrapper IService Interface to further encapsulate CRUD Use get to query a single row remove delete list query set page pagination Prefix naming distinguishes the Mapper layer to avoid confusion.
  • The generic T is any entity object
  • It is recommended that if there is a possibility to customize the general Service method, please create your own IBaseService to inherit the base class provided by Mybatis-Plus
  • Object Wrapper is Conditional constructor

save

/**
 * Insert a record (select fields, strategy insert)
 *
 * @param entity entity object
 */
boolean save(T entity);

saveBatch

/**
 * insert (bulk)
 *
 * @param entity entity object
 */
boolean saveBatch(T entity);

saveBatch

/**
* insert (bulk)
* 
* @param entityList collection of entity objects
* @param batchSize  Insert batch quantity
 */
boolean saveBatch(Collection<T> entityList, int batchSize);

saveOrUpdateBatch

/**
 * Bulk edit insert
 *
 * @param entityList collection of entity objects
 */
boolean saveOrUpdateBatch(Collection<T> entityList);

saveOrUpdateBatch

/**
 * Bulk edit insert
 *
 * @param entityList collection of entity objects
 * @param batchSize  each time
 */
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

removeById

/**
 * delete by ID
 *
 * @param id primary key ID
 */
boolean removeById(Serializable id);

removeByMap

/**
 * Delete records based on columnMap conditions
 *
 * @param columnMap table field map object
 */
boolean removeByMap(Map<String, Object> columnMap);

remove

/**
 * Delete records based on entity condition
 *
 * @param queryWrapper Entity Wrapper {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
boolean remove(Wrapper<T> queryWrapper);

removeByIds

/**
 * Delete (bulk delete by ID)
 *
 * @param idList List of primary key ID s
 */
boolean removeByIds(Collection<? extends Serializable> idList);

updateById

/**
 * Select edit by ID
 *
 * @param entity entity object
 */
boolean updateById(T entity);

update

/**
 * Update records based on whereEntity condition
 *
 * @param entity        entity object
 * @param updateWrapper Entity object wrapping operation class {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
 */
boolean update(T entity, Wrapper<T> updateWrapper);

updateBatchById

/**
 * Bulk update by ID
 *
 * @param entityList collection of entity objects
 * @param batchSize  Update batch quantity
 */
boolean updateBatchById(Collection<T> entityList, int batchSize);

saveOrUpdate

/**
 * TableId Note that there is an update record, whether to insert a record
 *
 * @param entity entity object
 */
boolean saveOrUpdate(T entity);

getById

/**
 * Query by ID
 *
 * @param id primary key ID
 */
T getById(Serializable id);

listByIds

/**
 * <p>
 * Query (Batch query based on ID)
 * </p>
 *
 * @param idList List of primary key ID s
 */
Collection<T> listByIds(Collection<? extends Serializable> idList);

listByMap

/**
 * Query (according to columnMap condition)
 *
 * @param columnMap table field map object
 */
Collection<T> listByMap(Map<String, Object> columnMap);

getOne

/**
 * According to Wrapper, query a record
 *
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 * @param throwEx      Whether there are multiple result s throwing an exception
 */
T getOne(Wrapper<T> queryWrapper, boolean throwEx);

getMap

/**
 * According to Wrapper, query a record
 *
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
Map<String, Object> getMap(Wrapper<T> queryWrapper);

getObj

/**
 * According to Wrapper, query a record
 *
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
Object getObj(Wrapper<T> queryWrapper);

count

/**
 * According to the Wrapper condition, query the total number of records
 *
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
int count(Wrapper<T> queryWrapper);

list

/**
 * query list
 *
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
List<T> list(Wrapper<T> queryWrapper);

page

/**
 * page turn query
 *
 * @param page         page turn object
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);

listMaps

/**
 * query list
 *
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);

listObjs

/**
 * Query all records according to Wrapper conditions
 *
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
List<Object> listObjs(Wrapper<T> queryWrapper);

pageMaps

/**
 * page turn query
 *
 * @param page         page turn object
 * @param queryWrapper Entity object encapsulation operation class {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

3. mapper layer option

Options are located under the package com.baomidou.mybatisplus.extension.injector.methods.additional Sql injector use.

int insertBatchSomeColumn(List<T> entityList);
int deleteByIdWithFill(T entity);

3. Conditional Constructor

[Simple Description]

  • The first input parameter boolean condition that appears below indicates whether the condition is added to the last generated sql
  • The multiple methods in the following code blocks complete the input parameters of individual boolean types from top to bottom, and the default is true
  • The generic Param appearing below are all subclass instances of Wrapper (all have all methods of AbstractWrapper)
  • The R in the input parameters of the following methods is generic, in ordinary wrapper is String, in LambdaWrapper is function (for example: Entity::getId,Entity is entity class, getId is getMethod of field id)
  • The R column in the input parameters of the following methods all represent database fields. When R is String, it is the database field name (the field name is the database keyword itself wrapped with escape characters), not the entity class data field name.
  • The following examples use ordinary wrapper s, and the input parameters of Map and List are expressed in json form!
  • If the Map or List of the input parameters is empty during use, it will not be added to the last generated sql.
  • If you have any questions, please click on the open source code, and you can also add lambda function.

[Note] It is not supported and deprecated to transmit Wrapper in RPC calls

  1. The wrapper is heavy.
  2. The transport wrapper is analogous to using a map to receive values ​​for your controller.
  3. The correct RPC call posture is to write a DTO for transmission, and the callee performs corresponding operations according to the DTO.

1. AbstractWrapper

Parent class of QueryWrapper(LambdaQueryWrapper) and UpdateWrapper(LambdaUpdateWrapper)
The where condition is used to generate sql, and the entity attribute is also used to generate the where condition of sql

Note: The where condition generated by entity has no associated behavior with the where condition generated by each api.

allEq

allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)

Description of individual parameters:

params : key is the database field name, value is the field value
null2IsNull : true to call when the value of the map is null isNull method, when false, ignore the value of null

  • Example 1: allEq({id:1,name:"Pharaoh",age:null})—>id = 1 and name = 'Pharaoh' and age is null
  • Example 2: allEq({id:1,name:"Pharaoh",age:null}, false)—>id = 1 and name = 'Pharaoh'
allEq(BiPredicate<R, V> filter, Map<R, V> params)
allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) 

Description of individual parameters:

filter : filter function, whether to allow the field to be passed into the comparison condition
params and null2IsNull : same as above

  • Example 1: alleq ((k, V) - > k.indexof ("a") > 0, {id:1, name: "Lao Wang", age:null}) - > name ='lao Wang 'and age is null
  • Example 2: allEq((k,v) -> k.indexOf("a") > 0, {id:1,name:"Pharaoh",age:null}, false)—>name = 'Pharaoh'

eq

eq(R column, Object val)
eq(boolean condition, R column, Object val)
  • is equal to =
  • Example: eq("name", "Pharaoh")—>name = 'Pharaoh'

ne

ne(R column, Object val)
ne(boolean condition, R column, Object val)
  • not equal to <>
  • Example: ne("name", "Pharaoh")—>name <> 'Pharaoh'

gt

gt(R column, Object val)
gt(boolean condition, R column, Object val)
  • greater than >
  • Example: gt("age", 18)—>age > 18

ge

ge(R column, Object val)
ge(boolean condition, R column, Object val)
  • Greater than or equal to >=
  • Example: ge("age", 18)—>age >= 18

lt

lt(R column, Object val)
lt(boolean condition, R column, Object val)
  • less than <
  • Example: lt("age", 18)—>age < 18

le

le(R column, Object val)
le(boolean condition, R column, Object val)
  • Less than or equal to <=
  • Example: le("age", 18)—>age <= 18

between

between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)
  • BETWEEN value 1 AND value 2
  • Example: between("age", 18, 30)—>age between 18 and 30

notBetween

notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)
  • NOT BETWEEN value 1 AND value 2
  • Example: notBetween("age", 18, 30)—>age not between 18 and 30

like

like(R column, Object val)
like(boolean condition, R column, Object val)
  • LIKE '%value%'
  • Example: like("name", "king")—>name like '%king%'

notLike

notLike(R column, Object val)
notLike(boolean condition, R column, Object val)
  • NOT LIKE '%value%'
  • Example: notLike("name", "King")—>name not like '%King%'

likeLeft

likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)
  • LIKE '%value'
  • Example: likeLeft("name", "King")—>name like '%King'

likeRight

likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)
  • LIKE 'value%'
  • Example: likeRight("name", "King")—>name like 'King%'

isNull

isNull(R column)
isNull(boolean condition, R column)
  • Field IS NULL
  • Example: isNull("name")—>name is null

isNotNull

isNotNull(R column)
isNotNull(boolean condition, R column)
  • Field IS NULL
  • Example: isNotNull("name")—>name is not null

in

in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)
  • Field IN (value.get(0), value.get(1), …)
  • Example: in("age",{1,2,3})—>age in (1,2,3)
in(R column, Object... values)
in(boolean condition, R column, Object... values)
  • Field IN (v0, v1, …)
  • Example: in("age", 1, 2, 3)—>age in (1,2,3)

notIn

notIn(R column, Collection<?> value)
notIn(boolean condition, R column, Collection<?> value)
  • Field IN (value.get(0), value.get(1), …)
  • Example: notIn("age",{1,2,3})—>age not in (1,2,3)
notIn(R column, Object... values)
notIn(boolean condition, R column, Object... values)
  • Field NOT IN (v0, v1, …)
  • Example: notIn("age", 1, 2, 3)—>age not in (1,2,3)

inSql

inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
  • Field IN (sql statement)
  • Example: inSql("age", "1,2,3,4,5,6")—>age in (1,2,3,4,5,6)
  • Example: inSql("id", "select id from table where id < 3")—>id in (select id from table where id < 3)

notInSql

notInSql(R column, String inValue)
notInSql(boolean condition, R column, String inValue)
  • Field NOT IN (sql statement)
  • Example: notInSql("age", "1,2,3,4,5,6")—>age not in (1,2,3,4,5,6)
  • Example: notInSql("id", "select id from table where id < 3")—>age not in (select id from table where id < 3)

groupBy

groupBy(R... columns)
groupBy(boolean condition, R... columns)
  • Grouping: GROUP BY fields, …
  • Example: groupBy("id", "name")—>group by id,name

orderByAsc

orderByAsc(R... columns)
orderByAsc(boolean condition, R... columns)
  • Sort: ORDER BY field, … ASC
  • Example: orderByAsc("id", "name")—>order by id ASC,name ASC

orderByDesc

orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
  • Sort: ORDER BY field, … DESC
  • Example: orderByDesc("id", "name")—>order by id DESC,name DESC

orderBy

orderBy(boolean condition, boolean isAsc, R... columns)
  • Sort: ORDER BY field, …
  • Example: orderBy(true, true, "id", "name")—>order by id ASC,name ASC

having

having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)
  • HAVING (sql statement)
  • Example: having("sum(age) > 10")—>having sum(age) > 10
  • Example: having("sum(age) > {0}", 11)—>having sum(age) > 11

or

or()
or(boolean condition)
  • splice OR

Precautions:

Actively calling or means that the next method is not connected with and! (If you do not call or, the default is to use and connect)

  • Example: eq("id",1).or().eq("name","Pharaoh")—>id = 1 or name = 'Pharaoh'
or(Function<Param, Param> func)
or(boolean condition, Function<Param, Param> func)
  • OR nesting
  • Example: or(i -> i.eq("name", "Li Bai").ne("status", "Alive"))—>or (name = 'Li Bai' and status <> 'Alive')

and

and(Function<Param, Param> func)
and(boolean condition, Function<Param, Param> func)
  • AND nesting
  • Example: and(i -> i.eq("name", "Li Bai").ne("status", "Alive"))—>and (name = 'Li Bai' and status <> 'Alive')

nested

nested(Function<Param, Param> func)
nested(boolean condition, Function<Param, Param> func)
  • normal nesting without AND or OR
  • Example: nested(i -> i.eq("name", "Li Bai").ne("status", "Alive"))—>(name = 'Li Bai' and status <> 'Alive')

apply

apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
  • splicing sql

Precautions:

This method can be used for database functions. The params of dynamic input parameters correspond to the {index} part in the previous applySql. In this way, there is no risk of sql injection, and vice versa!

  • Example: apply("id = 1")—>id = 1
  • Example: apply ("date_format (datecolumn,'%y-%m-%d') ='2008 -08-08') - >date_ format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
  • Example: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08") - >date_ format(dateColumn,'%Y-%m-%d') = '2008 -08-08'")

last

last(String lastSql)
last(boolean condition, String lastSql)
  • Ignore optimization rules and splicing directly to the end of sql

Note: It can only be called once, and the last one is subject to multiple calls. There is a risk of sql injection, please use it with caution.

  • Example: last("limit 1")

exists

exists(String existsSql)
exists(boolean condition, String existsSql)
  • Concatenate EXISTS (sql statement)
  • Example: exists("select id from table where age = 1")—>exists (select id from table where age = 1)

notExists

notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
  • Concatenate NOT EXISTS (sql statement)
  • Example: notExists("select id from table where age = 1")—>not exists (select id from table where age = 1)

2. QueryWrapper

[Simple Description]

Inherited from AbstractWrapper, its own internal property entity is also used to generate where conditions
and LambdaQueryWrapper, which can be obtained through the new QueryWrapper().lambda() method.

select

select(String... sqlSelect)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
  • Set query fields

[Simple Description]

The above methods are divided into two categories.
The second type of method is: filter the query fields (except the primary key), the entity attribute in the wrapper needs to have a value before the input parameter does not contain a class call, and the last time for repeated calls of these two types of methods shall prevail.

  • Example: select("id", "name", "age")
  • Example: select(i -> i.getProperty().startsWith("test"))

[Simple example]

@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo {
	
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect2(){
        IPage<User> page = selectUserPage();

        List<User> userList = page.getRecords();
        userList.forEach(user -> System.out.println("user:" + user));
    }

    // Query users with the name cuihua between the ages of 20-30
    public  IPage<User>  selectUserPage() {

        Page page = new Page<User>(1, 20);
		QueryWrapper<User> queryWrapper = new QueryWrapper<User>().between("age", 18, 20).eq("name", "cuihua");
		return userMapper.selectPage(page, queryWrapper);
	}
}

3. UpdateWrapper

illustrate:

Inherited from AbstractWrapper , its own internal property entity is also used to generate where conditions
and LambdaUpdateWrapper, which can be obtained through the new UpdateWrapper().lambda() method!

set

set(String column, Object val)
set(boolean condition, String column, Object val)
  • SQL SET fields
  • Example: set("name", "Old Litou")
  • Example: set("name", "")—>database field value becomes empty string
  • Example: set("name", null)—>database field value becomes null

setSql

setSql(String sql)
  • set SET part SQL
  • Example: set("name = 'Old Litou')

lambda

  • Get LambdaWrapper
    In QueryWrapper is to get LambdaQueryWrapper
    In UpdateWrapper is to get LambdaUpdateWrapper

4. Use Wrapper to customize SQL

[Source of demand]

After using mybatis-plus, what should I do if I want to use the convenience of Wrapper while customizing SQL? It has been perfectly solved in mybatis-plus version 3.0.7. The version needs to be greater than or equal to 3.0.7, and one of the following two solutions can be used.

Service.java

mysqlMapper.getAll(Wrappers.<MysqlData>lambdaQuery().eq(MysqlData::getGroup, 1));

Scheme 1 Annotation method Mapper.java

@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);

Scheme 2 XML form Mapper.xml

<select id="getAll" resultType="MysqlData">
	SELECT * FROM mysql_data ${ew.customSqlSegment}
</select>

4. Paging plugin

1. Simple example

<!-- spring xml Way -->
<plugins>
    <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
        <property name="sqlParser" ref="Custom parsing class, optional" />
        <property name="dialectClazz" value="Custom dialect class, optional" />
    </plugin>
</plugins>
//Spring boot method
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {

    /**
     * Pagination plugin
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

2. XML custom pagination

1) UserMapper class

public interface UserMapper{//Can inherit or not inherit BaseMapper
    /**
     * <p>
     * Query : Query the user list according to the state, and display it in pagination
     * Note!!: If there are multiple input parameters, you need to add an annotation to specify the parameter name to get the value in xml
     * </p>
     *
     * @param page Paging object, you can take values ​​from it in xml, pass the parameter Page That is automatic paging, it must be placed first (you can inherit Page to implement your own paging object)
     * @param state state
     * @return paging object
     */
    IPage<User> selectPageVo(Page page, @Param("state") Integer state);
}

2) UserMapper.xml is equivalent to writing an ordinary list query, and mybatis-plus automatically paginates.

<select id="selectPageVo" resultType="com.baomidou.cloud.entity.UserVo">
    SELECT id,name FROM user WHERE state=#{state}
</select>

3) UserServiceImpl class, call the paging method.

public IPage<User> selectUserPage(Page<User> page, Integer state) {
    // Do not perform count sql optimization to solve the problem that MP cannot automatically optimize SQL. At this time, you need to query the count part yourself
    // page.setOptimizeCountSql(false);
    // When total is non-zero (default is 0), the paging plugin will not perform count query
    // Point!! The object returned by pagination is the same object passed in
    return userMapper.selectPageVo(page, state);
}

3. Sequence primary key

Entity primary key supports Sequence

  • Oracle and other database primary key strategy configuration Sequence

  • GlobalConfiguration configure KeyGenerator

  GlobalConfiguration gc = new GlobalConfiguration();
  gc.setKeyGenerator(new OracleKeyGenerator());
@Bean
public OracleKeyGenerator oracleKeyGenerator(){
  return new OracleKeyGenerator();
}
  • The entity class configures the primary key Sequence,

    Specify the primary key @TableId(type=IdType.INPUT) // AUTO cannot be used

@TableName("TEST_SEQUSER")
@KeySequence("SEQ_TEST")//class annotation
public class TestSequser{
  @TableId(value = "ID", type = IdType.INPUT)
  private Long id;

}
  • Support the parent class to define @KeySequence, and the subclass to use it, so that several tables can share a Sequence
@KeySequence("SEQ_TEST")
public abstract class Parent{

}

public class Child extends Parent{

}

The above steps can use Sequence as the primary key.

Spring MVC: xml configuration, please refer to https://mp.baomidou.com/install

How to use Sequence as primary key, but entity primary key type is String

That is to say, the primary key of the table is varchar2, but the value needs to be taken from the sequence

  • 1. Entity definition @KeySequence annotation clazz specifies the type String.class
  • 2. The entity defines the type String of the primary key
@KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)
public class YourEntity{
    
    @TableId(value = "ID_STR", type = IdType.INPUT)
    private String idStr;
    ...
}
  • 3. Configure GlobalConfiguration.keyGenerator normally
@Bean
public GlobalConfiguration globalConfiguration() {
    GlobalConfiguration conf = new GlobalConfiguration();
    conf.setKeyGenerator(new OracleKeyGenerator());
    return conf;
}

MyBatis Plus common plugins

1. Logical deletion

Configured in the application.yml file in Spring Boot. If we keep the default, this configuration does not need to be written.

mybatis-plus:
  global-config:
    db-config:
      logic-delete-value: 1 # logical removed value (default 1)
      logic-not-delete-value: 0 # Logical undeleted value (default 0)

Then on the entity class, add the @TableLogic annotation.

@TableLogic
private Integer deleted;

[Note] Using mp's own method to delete and search will have tombstone function, but the xml written by yourself will not.

The simple understanding of java logical deletion is "deletion", which is a scheme to facilitate data recovery and protect the value of data itself. If you need to find out this data, you should not use logical deletion, but represent it as a state. For example, employee resignation, account lockout, etc. should be a status field, and logical deletion is not suitable. If you want to find and delete data, you need to write SQL separately to achieve, for example, you need to view the statistical summary information of all past data.

2. General enumeration

The main reason is to make MyBatis use enumeration properties more elegantly and to abandon those super cumbersome configurations.

As of version 3.1.1, it is better to use the default enum class directly.

Recommended configuration:

1) Using the IEnum interface, it is recommended to configure defaultEnumTypeHandler

2) Use annotation enumeration processing, it is recommended to configure typeEnumsPackage

3) Annotation enumeration processing and IEnum interface, it is recommended to configure typeEnumsPackage

4) Mixed with native enumeration, you need to configure defaultEnumTypeHandler and typeEnumsPackage

1. Two ways to use generic enum properties

Method 1: Annotate the enumeration property with @EnumValue

public enum GradeEnum {

    PRIMARY(1, "primary school"),  SECONDORY(2, "middle school"),  HIGH(3, "high school");

    GradeEnum(int code, String descp) {
        this.code = code;
        this.descp = descp;
    }

    @EnumValue//The value of the tag database is code
    private final int code;
    //. . . 
}

Method 2: Enumerate properties and implement IEnum interface

public enum AgeEnum implements IEnum<Integer> {
    ONE(1, "one year old"),
    TWO(2, "two years old"),
    THREE(3, "three years old");
    
    private int value;
    private String desc;
    
    @Override
    public Integer getValue() {
        return this.value;
    }
}

Entity properties use enumeration types

public class User{
    /**
     * name
     * Database field: name varchar(20)
     */
    private String name;
    
    /**
     * Age, enumeration handling of IEnum interface
     * Database field: age INT(3)
     */
    private AgeEnum age;
        
        
    /**
     * Grade, native enum (with {@link com.baomidou.mybatisplus.annotation.EnumValue}):
     * Database field: grade INT(2)
     */
    private GradeEnum grade;
}

2. Configure the scan general enumeration

Edit the Spring Boot project applicaton.yml file

mybatis-plus:
    # Support wildcard * or ; to split
    typeEnumsPackage: com.kkb.entity.enums

3. JSON serialization

1)Jackson

Just add the @JsonValue annotation to the get method that needs to respond to the description field

2)Fastjson

global approach

FastJsonConfig config = new FastJsonConfig();

// Set WriteEnumUsingToString config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString);

converter.setFastJsonConfig(config);

local treatment

@JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
private UserStatus status;

Choose one of the two, then override the toString() method on the enum.

3. Autofill function

When using it, you need to implement the meta object handler interface: com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

1. Configure @TableField annotation

The annotation fill field @TableField(.. fill = FieldFill.INSERT) can also be configured in the generator strategy section.

public class User {

    // Notice! This needs to be marked as a fill field
    @TableField(.. fill = FieldFill.INSERT)
    private String fillField;

    ....
}

To customize the implementation class, you need to implement the MetaObjectHandler interface. When the filling handler is used in Spring Boot, you need to add the @Component annotation.

You must use the setFieldValByName() or setInsertFieldValByName / setUpdateFieldValByName methods of the parent class, otherwise it will not be distinguished according to the FiledFill.xxx in the annotation.

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyMetaObjectHandler.class);

    @Override
    public void insertFill(MetaObject metaObject) {
        LOGGER.info("start insert fill ....");
        this.setFieldValByName("operator", "Jerry", metaObject);//Version number 3.0.6 and earlier
        //this.setInsertFieldValByName("operator", "Jerry", metaObject);//@since snapshot: 3.0.7.2-SNAPSHOT, @since official version has not released 3.0.7 yet
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        LOGGER.info("start update fill ....");
        this.setFieldValByName("operator", "Tom", metaObject);
        //this.setUpdateFieldValByName("operator", "Tom", metaObject);//@since snapshot: 3.0.7.2-SNAPSHOT, @since official version has not yet released 3.0.7
    }
}
public enum FieldFill {
    /**
     * Not processed by default
     */
    DEFAULT,
    /**
     * Insert fill field
     */
    INSERT,
    /**
     * Update fill field
     */
    UPDATE,
    /**
     * Insert and update populated fields
     */
    INSERT_UPDATE
}

4. SQL injector

Injector configuration: Globally configure sqlInjector to inject subclasses of the ISqlInjector interface to implement custom method injection.

The custom general method can implement the interface ISqlInjector , or inherit the abstract class AbstractSqlInjector to inject the general method SQL statement, and then inherit BaseMapper to add custom methods. Globally configuring sqlInjector to inject MP will automatically inject all the methods of the class into the mybatis container.

public interface ISqlInjector {

    /**
     * Check if SQL is injected (already injected and no longer injected)
     *
     * @param builderAssistant mapper information
     * @param mapperClass      mapper the class object of the interface
     */
    void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass);
}

5. Attack the SQL blocking parser

Main function: Prevent malicious full table update and delete.

@Bean
public PaginationInterceptor paginationInterceptor() {
    PaginationInterceptor paginationInterceptor = new PaginationInterceptor();

    ...

    List<ISqlParser> sqlParserList = new ArrayList<>();
    // Attack SQL to block the parser and join the parsing chain
    sqlParserList.add(new BlockAttackSqlParser());
    paginationInterceptor.setSqlParserList(sqlParserList);

    ...

    return paginationInterceptor;
}

Six, performance analysis plug-in

Profiling interceptor to output each SQL statement and its execution time. If you have high requirements for the printing effect of SQL, you can use the following [Execute SQL Analysis and Printing] plug-in.

First configure the <plugins> tag

<plugins>
    ....

    <!-- SQL Perform performance analysis, use in development environment, online is not recommended. maxTime Refers sql maximum execution time -->
    <plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
        <property name="maxTime" value="100" />
        <!--SQL format default false-->
        <property name="format" value="true" />
    </plugin>
</plugins>

Then, in Spring Boot use

//Spring boot method
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {

    /**
     * SQL Execution efficiency plugin
     */
    @Bean
    @Profile({"dev","test"})// Set the dev test environment to open
    public PerformanceInterceptor performanceInterceptor() {
        return new PerformanceInterceptor();
    }
}

Parameter Description

  • Parameter: maxTime The maximum time for SQL execution, beyond which it will automatically stop running, which is helpful to find problems.

  • Parameters: format SQL Whether to format the SQL, the default is false.

**[**Note] This plugin is only used in the development environment and is not recommended for use in the production environment.

7. Execute SQL analysis and print

In version 3.1.0+, this function relies on the p6spy component, which perfectly outputs and prints SQL and execution time.

1) Introduce p6spy dependency in Maven

<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>The latest version of</version>
</dependency>

2) Configure the application.yml file

spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:h2:mem:test
    ...

3) Configure the spy.properties file

module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# Custom log printing
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#log output to console
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# log sql using the logging system
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# Set p6spy driver proxy
deregisterdrivers=true
# Unprefix JDBC URL
useprefix=true
# With the exception of the configuration record Log, the result sets that can be removed are error, info, batch, debug, statement, commit, rollback, result, and resultset.
excludecategories=info,debug,result,batch,resultset
# date format
dateformat=yyyy-MM-dd HH:mm:ss
# The actual drive can be multiple
#driverlist=org.h2.Driver
# Whether to enable slow SQL logging
outagedetection=true
# Slow SQL logging standard 2 seconds
outagedetectioninterval=2

[Notice]

​ driver-class-name is the driver class provided by p6spy.

​ The url prefix is ​​jdbc:p6spy followed by a colon as the corresponding database connection address.

​ ❤ This plugin has performance loss and is not recommended for production environments.

8. Optimistic lock plugin

1. Main application scenarios

Intent: When a record is to be updated, I hope that this record has not been updated by others

Optimistic locking implementation:

  • When fetching a record, get the current version
  • When updating, bring this version
  • When performing an update, set version = newVersion where version = oldVersion
  • If the version is wrong, the update fails

2. How to use

Plugin configuration

Configuration in Spring Boot

@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
}

2) Be sure to use the @Version annotation on the entity field

@Version
private Integer version;
Special Note
  • Supported data types: int, Integer, long, Long, Date, Timestamp, LocalDateTime
  • In integer type newVersion = oldVersion + 1
  • newVersion will be written back to entity
  • Only support updateById(id) and update(entity, wrapper) methods
  • Under the update(entity, wrapper) method, wrapper cannot be reused!!!
Example

Write the Java code as follows:

int id = 100;
int version = 2;

User u = new User();
u.setId(id);
u.setVersion(version);
u.setXXX(xxx);

if(userService.updateById(u)){
    System.out.println("Update successfully");
}else{
    System.out.println("Update failed due to modified by others");
}

The SQL principle corresponding to the above is very simple:

update tbl_user set name = 'update',version = 3 where id = 100 and version = 2

Nine, dynamic data sources

1. Quick understanding

1 Introduction

dynamic-datasource-spring-boot-starter: A Springboot-based starter for quickly inheriting multiple data sources.

github address: https://github.com/baomidou/dynamic-datasource-spring-boot-starter

2. Advantages

Regarding the switching of dynamic data sources, there are only two cores.

1) The advantage of building multiple environments is that it is easy to control and integrate some simple distributed transactions. The disadvantage is that there is a large amount of non-dynamic code at the same time, and the configuration is difficult.

2) Based on the native AbstractRoutingDataSource provided by spring, refer to some documents to implement the switch yourself.

If you have few data sources and the scene is not complicated, you can choose any of the above. If you need more features, try this dynamic data source.

1)Data source grouping, suitable for multiple scenarios, pure multi-database, read-write separation, one master and multiple slaves, and mixed mode.

2)Simple integration Druid Data source monitoring multiple data sources, simple integration Mybatis-Plus Simplified single form, simple integration P6sy format sql,Simple integration Jndi data source.

3)simplify Druid and HikariCp Configuration, which provides global parameter configuration.

4)Provide a custom data source source (the default use yml or properties configuration).

5)Data sources can be dynamically increased or decreased after the project is started.

6)use spel Dynamic parameter parsing data sources, such as from session,header and parameters to get the data source. (Multi-tenant architecture artifact)

7)Multi-level data source nesting switch. (a business ServiceA transfer ServiceB,ServiceB transfer ServiceC,each Service are different data sources)

8)use regex or spel expression to switch data sources (experimental).

3. Disadvantages

Multiple data source transactions cannot be used (transactions can be used under the same data source), and other solutions on the Internet cannot be provided.

If you need to use distributed transactions, it's time to microservice your architecture.

4. Agreement

1) This framework only does the core thing of switching data sources, and does not limit your specific operations. After switching data sources, you can do any CRUD.

2) The header of all data sources separated by underscore _ in the configuration file is the name of the group, and the data sources with the same group name will be placed under a group.

3) Switching the data source can be either the group name or the specific data source name. By default, the load balancing mechanism is used for switching.

4) The default data source name is master, you can modify it through spring.datasource.dynamic.primary.

5) Annotations on methods take precedence over annotations on classes.

5. Recommendations

It is strongly recommended to follow general rules in master-slave mode so that others can more easily understand your code.

The primary database recommends performing only INSERT UPDATE DELETE operations.

It is recommended to perform only SELECT operations from the database.

2. Specific use

1) Introduce dependencies

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>

2) Configure the data source

spring:
  datasource:
    dynamic:
      primary: master #Set the default data source or data source group, the default value is master
      datasource:
        master:
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
        slave_1:
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
        slave_2:
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://xx.xx.xx.xx:3308/dynamic
       #...omit
       #The above will configure a default library master, and there are two sub-libraries slave_1,slave_2 under a group slave
# Multi-master multi-slave pure multi-library (remember to set primary) mixed configuration
spring:       spring:                spring:
  datasource:   datasource:            datasource:
    dynamic:      dynamic:               dynamic:
      datasource:   datasource:            datasource:
        master_1:     mysql:                  master:
        master_2:     oracle:                 slave_1:
        slave_1:      sqlserver:              slave_2:
        slave_2:      postgresql:             oracle_1:
        slave_3:      h2:                     oracle_2:

3) Use @DS to switch data sources.

@DS can be annotated on methods and classes, and method annotations take precedence over class annotations.

[Note] Annotation is on the service implementation or mapper interface method, but it is strongly not recommended to annotate both service and mapper at the same time. (might be problematic)

annotationresult
no @DSDefault data source
@DS("dsName")dsName can be the group name or the name of a specific library
@Service
@DS("slave")
public class UserServiceImpl implements UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public List<Map<String, Object>> selectAll() {
    return  jdbcTemplate.queryForList("select * from user");
  }
  
  @Override
  @DS("slave_1")
  public List<Map<String, Object>> selectByCondition() {
    return  jdbcTemplate.queryForList("select * from user where age >10");
  }
}

It can also be annotated at the mapper interface layer in the mybatis environment.

@DS("slave")
public interface UserMapper {

  @Insert("INSERT INTO user (name,age) values (#{name},#{age})")
  boolean addUser(@Param("name") String name, @Param("age") Integer age);

  @Update("UPDATE user set name=#{name}, age=#{age} where id =#{id}")
  boolean updateUser(@Param("id") Integer id, @Param("name") String name, @Param("age") Integer age);

  @Delete("DELETE from user where id =#{id}")
  boolean deleteUser(@Param("id") Integer id);

  @Select("SELECT * FROM user")
  @DS("slave_1")
  List<User> selectAll();
}

You can also refer to more detailed content: https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/wikis/pages

10. Multi-tenant SQL parser

This is used in conjunction with the paging interceptor. The spring boot example is configured as follows.

@Bean
public PaginationInterceptor paginationInterceptor() {
    PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    paginationInterceptor.setLocalPage(true);// Enable PageHelper support
    /*
     * [Test Multi-tenancy] SQL Parse Processing Interceptor<br>
     * It is fixed here as household 1. You can read it from the cookie, so the data cannot see the record [ Twisted Vine ] (note the SQL)<br>
     */
    List<ISqlParser> sqlParserList = new ArrayList<>();
    TenantSqlParser tenantSqlParser = new TenantSqlParser();
    tenantSqlParser.setTenantHandler(new TenantHandler() {
        @Override
        public Expression getTenantId() {
            return new LongValue(1L);
        }

        @Override
        public String getTenantIdColumn() {
            return "tenant_id";
        }

        @Override
        public boolean doTableFilter(String tableName) {
            // Here you can determine whether to filter the table
            /*
            if ("user".equals(tableName)) {
                return true;
            }*/
            return false;
        }
    });
    sqlParserList.add(tenantSqlParser);
    paginationInterceptor.setSqlParserList(sqlParserList);
    paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
        @Override
        public boolean doFilter(MetaObject metaObject) {
            MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
            // Filter the custom query, there is no tenant information constraint [ Twisted Vine ] appears at this time
            if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {
                return true;
            }
            return false;
        }
    });
    return paginationInterceptor;
}

11. Dynamically indicate SQL parsing

Implement the ITableNameHandler interface and inject it into the DynamicTableNameParser processor chain, and inject the dynamic table name parser into the MP parsing chain.

[Note] The principle is to parse and replace the set table name as the returned table name of the processor. It is suggested that the table name can be defined more complex to avoid mistaken substitution. For example: the real table name is set to mp_dt_user and the processor is replaced by user_2019, etc.

12. MyBatisX rapid development plug-in

MybatisX is a rapid development plug-in based on IDEA, born for efficiency.

Installation method: Open IDEA, go to File -> Settings -> Plugins -> Browse Repositories, enter mybatisx to search and install.

Project code cloud address: https://gitee.com/baomidou/MybatisX

Function:

1) Java and XML call back jump

2) The Mapper method automatically generates MXL

Tags: Java

Posted by davidwhiteuk on Fri, 29 Jul 2022 02:18:00 +0930