Mybatis – Plus detailed use tutorial

Java public welcome attention blogger, “master”, focus on dry share Java industry articles www.javaman.cn/jszw/mybati…

What is Mybatis – Plus

MyBatis-Plus (opens new window)MP for short is aMyBatis (opens new window)Enhancement tools, on the basis of MyBatis only do enhancement do not change, to simplify development, improve efficiency and born.

features

  • No intrusion: only enhancements are made, no changes are made, and its introduction will not affect the existing project, as smooth as silk
  • Low loss: Basic CURD will be injected automatically upon startup, with basically no loss in performance and direct object-oriented operation
  • Powerful CRUD operations: built-in universal Mapper, universal Service, only through a small amount of configuration can achieve a single table most CRUD operations, more powerful condition constructor, to meet all types of use requirements
  • Support Lambda form call: through Lambda expressions, it is convenient to write all kinds of query conditions, without worrying about field write errors
  • Support automatic generation of primary keys: support up to four primary key policies (including distributed unique ID generator – Sequence), can be freely configured, perfect solution to the primary key problem
  • Support for ActiveRecord mode: Support for ActiveRecord form calls, entity classes only need to inherit from Model classes to perform powerful CRUD operations
  • Support custom global universal operations: support Write once (use anywhere)
  • Built-in code generator: using code or Maven plug-in can quickly generate Mapper, Model, Service, Controller layer code, support template engine, more than a lot of custom configuration you to use
  • Built-in paging plug-in: Based on MyBatis physical paging, developers do not need to care about specific operations, after configuring the plug-in, write paging is equal to ordinary List query
  • The paging plug-in supports a variety of databases: MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer, etc
  • Built-in performance analysis plug-in: outputs SQL statements and their execution time. It is recommended to enable this function during development and testing to quickly find out slow queries
  • Built-in global interception plug-in: provides intelligent analysis and blocking of delete and UPDATE operations on all tables, and can customize interception rules to prevent misoperations

Quick start

steps

1, create database and database table

(Examples from the official website)

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 'email'.PRIMARY KEY (id)
);
Copy the code

Insert table data

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1.'Jone'.18.'[email protected]'),
(2.'Jack'.20.'[email protected]'),
(3.'Tom'.28.'[email protected]'),
(4.'Sandy'.21.'[email protected]'),
(5.'Billie'.24.'[email protected]');
Copy the code

3. Create a Springboot project and introduce mubatis- Plus dependency

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.1.0</version>
</dependency>
Copy the code

Application. Yml configure the mysql data source

# DataSource Config
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost/mp_plus? serverTimezone=GMT%2B8&characterEncoding=UTF-8&allowMultiQueries=true
    username: root
    password: 123456
Copy the code

5. Use Mybatis – Plus

  • entity

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        @TableId(type = IdType.AUTO) // Use database autoincrement
        private long id;
        private String name;
        private int age;
        private String email;
    }
    Copy the code
  • Mapper interfaces

// Inherits the base class BaseMapper from the corresponding Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
	// All CRUD operations have been written
}
Copy the code
  • The SpringBoot test boot class adds the @mapperscan scan annotation

    @MapperScan("com.mpstudy.mp.mapper")
    Copy the code
  • test

@MapperScan("com.mpstudy.mp.mapper")
@SpringBootTest
class MpApplicationTests {
    
    // BaseMapper inherits all methods from its parent class
    // We can also write our own extension methods!
    @Autowired
    UserMapper userMapper;

    @Test
    void contextLoads(a) {
        List<User> users = userMapper.selectList(null); // The conditional constructor is not usedusers.forEach(System.out::println); }}Copy the code
  • The test results
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75023c53]
User(id=1, name=Jone, age=18, [email protected], createTime=null, updateTime=null)
User(id=2, name=Jack, age=20, [email protected], createTime=null, updateTime=null)
User(id=3, name=Tom, age=28, [email protected], createTime=null, updateTime=null)
User(id=4, name=Sandy, age=21, [email protected], createTime=null, updateTime=null)
User(id=5, name=Billie, age=24, [email protected], createTime=null, updateTime=null)
Copy the code

The log configuration

Print out the SQL we execute for easy debugging

1. Add the configuration in application.yml

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl // Standard output
Copy the code

2. After the log configuration is complete, corresponding logs will be generated on the console

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75023c53] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@688197093 wrapping com.mysql.jdbc.JDBC4Connection@3610f277] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email,create_time,update_time FROM user 
==> Parameters: 
<==    Columns: id, name, age, email, create_time, update_time
<==        Row: 1, Jone, 18, [email protected], null, null
<==        Row: 2, Jack, 20, [email protected], null, null
<==        Row: 3, Tom, 28, [email protected], null, null
<==        Row: 4, Sandy, 21, [email protected], null, null
<==        Row: 5, Billie, 24, [email protected], null, null
Copy the code

CRUD,

1. Insert operation

@Test
void insert(a){
    User user = new User();
    user.setName("Master Java 1");
    user.setAge(11);
    user.setEmail("[email protected]");
    int insert = userMapper.insert(user);
}
Copy the code

In the above example, the User id is not inserted. In the User class, it is set to automatic, that is, to follow the configuration of the database.

// Primary key policy supported by mybatis- Plus
public enum IdType {
    AUTO(0), // The database ID is automatically increased
    NONE(1), // The primary key is not set
    INPUT(2), // Enter it manually
    ID_WORKER(3), // The default global unique ID
    UUID(4), // Globally unique ID UUID
    ID_WORKER_STR(5);  //ID_WORKER string notation
Copy the code

2. Update operation

@Test
void update(a){
    User user = new User();
    user.setId(6);
    user.setAge(38);
    int i = userMapper.updateById(user); // An object is passed in
    System.out.println(i);
}
Copy the code

3. The timestamp is automatically filled

In real development, we want all create_time and update_time to be automatic, without human modification or maintenance

There are two implementation methods:

Method 1: Database level (add default field to database)

Method two: code level

  • Write time-stamped auto-fill annotations

    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    @Slf4j
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("Start filling");
            this.setFieldValByName("createTime".new Date(),metaObject);
            this.setFieldValByName("updateTime".new Date(),metaObject);
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("Update fill");
            this.setFieldValByName("updateTime".newDate(),metaObject); }}Copy the code
  • Add a TableField annotation to the fields of the entity class

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        @TableId(type = IdType.AUTO)
        private long id;
        private String name;
        private int age;
        private String email;
    
        // Create time annotations
        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
    	// Modify the time annotation
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    }
    Copy the code
  • test

    @Test
    void insert(a){
        User user = new User();
        user.setName("Public account: Java Master");
        user.setAge(2);
        user.setEmail("[email protected]");
        int insert = userMapper.insert(user);
    }
    Copy the code

4. Delete operations

// Test delete
@Test
public void testDeleteById(a){
	userMapper.deleteById(1);
} 

// Batch delete by id
@Test
public void testDeleteBatchId(a){
	userMapper.deleteBatchIds(Arrays.asList(2.3.4));
} 

// Delete using map
@Test
public void testDeleteMap(a){
HashMap<String, Object> map = new HashMap<>();
map.put("name"."Master Java");
userMapper.deleteByMap(map);
Copy the code

5. Logical deletion

Compared with physical delete, we need to save the data, so we need to save the deleted data, which uses logical delete

Physical delete: Removes a logical delete from a database directly: invalidates a variable instead of removing it from the database! sfyx = 0 => sfyx = 1

  • Add sFYX (valid or not) fields to the database table

  • The entity class adds attributes

    @TableLogic // Logical delete
    private Integer sfyx;
    Copy the code
  • Configuration logic removes components and is managed by SpringBoot

    // Logical delete component!
    @Bean
    public ISqlInjector sqlInjector(a) {
    	return new LogicSqlInjector();
    }
    Copy the code
  • Modify application.yml to add configuration

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      global-config:
        db-config:
          logic-delete-value: 0  / / deleted
          logic-not-delete-value: 1  / / not deleted
    Copy the code
  • Test (see that while the DELETE method is executed, the actual SQL is an UPDATE statement)

    @Test
        void testDelete(a){
            int i = userMapper.deleteById(2);
        }
    Copy the code
    Creating a new SqlSession
    SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@456bcb74] was not registered for synchronization because synchronization is not active
    JDBC Connection [HikariProxyConnection@66747889 wrapping com.mysql.jdbc.JDBC4Connection@4dd90166] will not be managed by Spring
    ==>  Preparing: UPDATE user SET sfyx=0 WHERE id=? AND sfyx=1 
    ==> Parameters: 2(Integer)
    <==    Updates: 1
    Copy the code

6, paging query

  • Configure a page interceptor

    @Bean
    public PaginationInterceptor paginationInterceptor(a){
        return new PaginationInterceptor();
    }
    Copy the code
  • Using Page objects

    @Test
    void testPage(a){
        Page<User> page = new Page<>(1.3);
        userMapper.selectPage(page, null);
        page.getRecords().forEach(System.out::println);
        System.out.println(page.getTotal());
    }
    Copy the code

7, multiple table query

  • Create a VO object

    import com.mpstudy.mp.entity.User;
    import lombok.Data;
    
    @Data
    public class UserClassVo extends User {
        private String className;
    }
    Copy the code
  • Add getAllUsers method to UserMapper, write SQL by select annotation

@Repository
public interface UserMapper extends BaseMapper<User> {
    @Select("select a.*,b.name as class_name from user a,class b,user_class c where a.id=c.user_id and b.id=c.class_id ")
    List<UserClassVo> getAllUsers(a);
}
Copy the code
  • test

    @Test
    void testGetAllUsers(a){
        List<UserClassVo> allUsers = userMapper.getAllUsers();
        allUsers.forEach(System.out::println);
    }
    Copy the code
Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@47f04e4d] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@843410864 wrapping com.mysql.jdbc.JDBC4Connection@5f5827d0] will not be managed by Spring ==> Preparing: select a.*,b.name as class_name from user a,class b,user_class c where a.id=c.user_id and b.id=c.class_id ==> Parameters: <== Columns: id, name, age, email, create_time, update_time, sfyx, class_name <== Row: 2, Jack, 20, [email protected], null, 2021-09-20 18:05:06.0, 0, small 2 class <== Row: 3, Tom 28, [email protected], NULL, 2021-09-20 18:04:27.0, 1, Class 1Copy the code

8, multi-table paging query

  • Create a VO object

    import com.mpstudy.mp.entity.User;
    import lombok.Data;
    
    @Data
    public class UserClassVo extends User {
        private String className;
    }
    Copy the code
  • Add the getUsersByPage method to UserMapper and write SQL with select annotations

    @Repository
    public interface UserMapper extends BaseMapper<User> {
        // Pass in the IPage object and the QueryWrapper conditional constructor
        ${ew.customSQLSegment} ${ew.customsqlSegment
        @Select("select a.*,c.name as class_name from user a left join user_class b on a.id=b.user_id left join class c on b.class_id = c.id " + "${ew.customSqlSegment} ")
        IPage<UserClassVo> getUsersByPage(IPage<UserClassVo> page,@Param(Constants.WRAPPER) QueryWrapper wrapper);
    }
    Copy the code
  • test

    @Test
    void testGetUsersByPage(a){
        Page<UserClassVo> page = new Page<>(2.2);
        QueryWrapper<UserClassVo> wrapper = new QueryWrapper<>();
        wrapper.likeRight("a.name"."java");
        userMapper.getUsersByPage(page,wrapper);
        page.getRecords().forEach(System.out::println);
    }
    Copy the code
Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f438904] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@766089249 wrapping com.mysql.jdbc.JDBC4Connection@62cba181] will not be managed by Spring JsqlParserCountOptimize sql=select a.*,c.name as class_name from user a left join user_class b on a.id=b.user_id left join class c on b.class_id = c.id WHERE a.name LIKE  ? ==> Preparing: SELECT COUNT(1) FROM user a LEFT JOIN user_class b ON a.id = b.user_id LEFT JOIN class c ON b.class_id = c.id WHERE a.name LIKE ? ==> Parameters: java%(String) <== Columns: COUNT(1) <== Row: 3 ==> Preparing: select a.*,c.name as class_name from user a left join user_class b on a.id=b.user_id left join class c on b.class_id = c.id WHERE a.name LIKE ? LIMIT ?,? ==> Parameters: java%(String), 0(Long), 2(Long) <== Columns: id, name, age, email, create_time, update_time, sfyx, class_name <== Row: 6, Java Master, 38, [email protected], null, 2021-09-20 18:04:29.0, 1, small 2 class <== Row: 7, Java Master, 11, [email protected], null, 2021-09-20 18:04:29.0, 1, big class <== Total: 2Copy the code

Conditional constructor

Description:

The parent classes of QueryWrapper(LambdaQueryWrapper) and UpdateWrapper(LambdaUpdateWrapper) are used to generate WHERE conditions for SQL, Entity attributes are also used to generate SQL WHERE conditions Note: The WHERE conditions generated by entity do not have any behavior associated with where conditions generated using the various apis

Support: allEq, eq, isNull etc, specific see: conditions for the constructor | MyBatis – Plus (baomidou.com)

  • test
@Test
void testWrapper01(a){
    Query the user whose name is not empty and whose mailbox is not empty and whose age is greater than or equal to 12 records
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name").isNotNull("create_time").gt("age".12);
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

@Test
void testWrapper02(a){
    Select * from age 18 to 24
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age".18.24);
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

@Test
void testWrapper03(a){
    // Record with Java in the name
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.likeRight("name"."java");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}
Copy the code

11. Performance analysis plug-in

In the actual work, you may encounter some cases of slow SQL. The performance analysis plug-in can locate the slow SQL and its running time

  • The import plug-in

    @Bean
    @Profile({"dev"}) // The development environment runs
    public PerformanceInterceptor performanceInterceptor(a){
        PerformanceInterceptor interceptor = new PerformanceInterceptor();
        interceptor.setFormat(true); // Format the SQL
        interceptor.setMaxTime(20);  // Set the timeout period in milliseconds
        return interceptor;
    }
    Copy the code
  • Application.xml is set to dev development mode

    # DataSource Config
    spring:
      profiles:
        active: dev
    Copy the code
  • Test (the last 1 line of the log shows a timeout error)

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException:  The SQL execution time is too large, please optimize ! 
    ### The error may exist in com/mpstudy/mp/mapper/UserMapper.java (best guess)
    ### The error may involve com.mpstudy.mp.mapper.UserMapper.getUsersByPage
    ### The error occurred while handling results
    ### SQL: select a.*,c.name as class_name from user a left join user_class b on a.id=b.user_id left join class c on b.class_id = c.id WHERE a.name LIKE ? LIMIT ?,?
    ### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException:  The SQL execution time is too large, please optimize ! 
    Copy the code

12. Code automatic generator

Dao, Entity, Service, Controller Automatic generation AutoGenerator is MyBatis-Plus code generator, The AutoGenerator can quickly generate the code of Entity, Mapper, Mapper XML, Service, Controller and other modules, greatly improving the development efficiency.

  • Import dependence

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.1.0</version>
    </dependency>
    Copy the code
  • Code generation

    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.annotation.FieldFill;
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.InjectionConfig;
    import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
    import com.baomidou.mybatisplus.generator.config.GlobalConfig;
    import com.baomidou.mybatisplus.generator.config.PackageConfig;
    import com.baomidou.mybatisplus.generator.config.StrategyConfig;
    import com.baomidou.mybatisplus.generator.config.po.TableFill;
    import com.baomidou.mybatisplus.generator.config.rules.DateType;
    import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;
    
    // Automatic code generator
    public class CodeGenerator {
    
        public static void main(String[] args) {
            // Build a code automatic generator object
            AutoGenerator autoGenerator = new AutoGenerator();
    
            //1
            GlobalConfig gc = new GlobalConfig();
            String oPath = System.getProperty("user.dir");// Get the path to the current project
            gc.setOutputDir(oPath + "/src/main/java");   // Generate file output root directory
            gc.setOpen(false);// No file box is displayed after the file is generated
            gc.setFileOverride(true);  // File overwrite
            gc.setAuthor("ryan");/ / the author
            gc.setServiceName("%sService");    // Remove the I prefix of Service
            gc.setIdType(IdType.ID_WORKER);
            gc.setDateType(DateType.ONLY_DATE);
            gc.setSwagger2(true);
            autoGenerator.setGlobalConfig(gc);
    
            //2. Configure the data source
            DataSourceConfig dsc = new DataSourceConfig();
            dsc.setDbType(DbType.MYSQL);   // Set the database type
            dsc.setUrl("jdbc:mysql://localhost:3306/mp_plus? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");  // Specify the database
            dsc.setDriverName("com.mysql.jdbc.Driver");
            dsc.setUsername("root");
            dsc.setPassword("root");
            autoGenerator.setDataSource(dsc);
    
    
    
            // Package configuration
            PackageConfig pc = new PackageConfig();
            pc.setModuleName("mp");
            pc.setParent("com.mpstudy");
            pc.setEntity("pojo");
            pc.setMapper("mapper");
            pc.setService("service");
            pc.setController("controller");
            autoGenerator.setPackageInfo(pc);
    
            // 4
            StrategyConfig strategy = new StrategyConfig();
            strategy.setInclude("user"."user_class"."class"); // Set the name of the table to be mapped
            strategy.setNaming(NamingStrategy.underline_to_camel);
            strategy.setColumnNaming(NamingStrategy.underline_to_camel);
            strategy.setEntityLombokModel(true); // Automatic lombok;
            strategy.setLogicDeleteFieldName("deleted");
            strategy.setTablePrefix("tb_"); // Remove the table prefix
            // Automatically populate the configuration
            TableFill gmtCreate = new TableFill("create_time", FieldFill.INSERT);
            TableFill gmtModified = new TableFill("update_time", FieldFill.INSERT_UPDATE);
            ArrayList<TableFill> tableFills = new ArrayList<>();
            tableFills.add(gmtCreate);
            tableFills.add(gmtModified);
            strategy.setTableFillList(tableFills);
            / / optimistic locking
            strategy.setVersionFieldName("version"); strategy.setRestControllerStyle(true);
            strategy.setControllerMappingHyphenStyle(true);
            autoGenerator.setStrategy(strategy);
    
            // not adding this will result in a null pointer exception
            InjectionConfig injectionConfig = new InjectionConfig() {
                // Custom attribute injection: ABC
                // In the.ftl(or.vm) template, get the attributes with ${cfg. ABC}
                @Override
                public void initMap(a) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("abc".this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                    this.setMap(map); }};// Custom configuration
            autoGenerator.setCfg(injectionConfig);
    
            // Perform the buildautoGenerator.execute(); }}Copy the code