简介

MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

快速开始

参照官网快速开始的样例,官网用的是h2数据库,我这里用的是Mysql。

  1. 创建数据库schema,并执行以下脚本。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    DROP TABLE IF EXISTS user;

    CREATE TABLE user
    (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
    );

    DELETE FROM user;

    INSERT INTO user (id, name, age, email) VALUES
    (1, 'Jone', 18, 'test1@baomidou.com'),
    (2, 'Jack', 20, 'test2@baomidou.com'),
    (3, 'Tom', 28, 'test3@baomidou.com'),
    (4, 'Sandy', 21, 'test4@baomidou.com'),
    (5, 'Billie', 24, 'test5@baomidou.com');
  2. 添加依赖

    主要的依赖。

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.1</version>
    </dependency>

    测试依赖如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!--数据库驱动-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--lombok-->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
    </dependency>
    <!--mybatis-plus-->
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.1</version>
    </dependency>
    <!--单元测试-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
    <exclusion>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    </exclusion>
    </exclusions>
    </dependency>
    </dependencies>
  3. 创建一个实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.yw.pojo;
    import lombok.Data;

    @Data
    public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    }
  4. 创建Mapper接口

    1
    2
    3
    4
    5
    6
    7
    8
    package com.yw.Mapper;

    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.yw.pojo.User;
    import org.springframework.stereotype.Repository;

    @Repository
    public interface UserMapper extends BaseMapper<User> {}
  5. 配置数据源和日志

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 配置MySQL
    spring:
    datasource:
    username: root
    password: password
    url: jdbc:mysql://localhost:3306/schema?userUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 配置日志,以便更好查看结果
    mybatis-plus:
    configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  6. 添加扫描Mapper文件的注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.yw;

    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    @SpringBootApplication
    @MapperScan("com.yw.Mapper")
    public class DemoApplication {
    public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
    }
    }
  7. 测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.yw;

    import com.yw.Mapper.UserMapper;
    import com.yw.pojo.User;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import java.util.List;

    @SpringBootTest
    class DemoApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
    List<User> userList = userMapper.selectList(null);
    System.out.println(userList);
    }

    }

然后就完成了。这的确比mybatis简单了不少。

mybatis大概流程是导入依赖,定义实体类,定义mapper接口,创建绑定mapper接口的xml配置文件,配置数据源并绑定mybatis的配置文件和实体类,然后才能使用。现在简单了很多。

主键生成策略

雪花算法

主键生成策略有很多,不过主要学习一下这个雪花算法。

首先来做个插入的测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.yw;

import com.yw.Mapper.UserMapper;
import com.yw.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

@Autowired
private UserMapper userMapper;
//用的还是官网给的测试数据库
@Test
public void testInsert() {
User user = new User();
user.setAge(20);
user.setEmail("1141950370@qq.com");
user.setName("yww");
userMapper.insert(user);
}
}

这个测试是没有设置主键id的值的,来看一下主键的生成。

插入成功之后看到数据是这样的。

这么一长串的id是什么东西呢,我们在官网看到主键有个注释@TableId,点进去看看源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.baomidou.mybatisplus.annotation;
import java.lang.annotation.*;
/**
* 表主键标识
*
* @author hubin
* @since 2016-01-23
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface TableId {
/**
* 字段名(该值可无)
*/
String value() default "";
/**
* 主键类型
* {@link IdType}
*/
IdType type() default IdType.NONE;
}

这里就可以看到主键是可以可无的,下面是默认的主键生成策略,我们在点IdType进去看看有什么策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.baomidou.mybatisplus.annotation;
import lombok.Getter;

/**
* 生成ID类型枚举类
*
* @author hubin
* @since 2015-11-10
*/
@Getter
public enum IdType {
/**
* 数据库ID自增
* <p>该类型请确保数据库设置了 ID自增 否则无效</p>
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),

/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER_STR(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
*/
@Deprecated
UUID(4);

private final int key;

IdType(int key) {
this.key = key;
}
}

在这里就看到当主键为空的时候,会有一个默认的生成策略ASSIGN_ID,这个策略的默认实现算法是雪花算法

雪花算法

总结

官网已经将上述方法已经总结的很好了,我就直接放官方的表了。

描述
AUTO 数据库ID自增(需要数据库也要设置为自增)
NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUT insert前自行set主键值
ASSIGN_ID 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUID 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)
ID_WORKER 分布式全局唯一ID 长整型类型(please use ASSIGN_ID)
UUID 32位UUID字符串(please use ASSIGN_UUID)
ID_WORKER_STR 分布式全局唯一ID 字符串类型(please use ASSIGN_ID)

从官网的删除线和源码来看,后面三种是已经不支持使用了的。

默认的生成策略是NONE,相当于INPUT,就是自己设置主键的值,当主键没有设值的时候,默认是使用ASSIGN_ID雪花算法。

代码生成器

首先需要引入依赖。

1
2
3
4
5
6
7
8
9
10
11
12
<!--   代码生成器    -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus-generator.version}</version>
</dependency>
<!-- 代码生成器的引擎 velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>

引擎可以选择其他的,这里就使用velocity

这里给出一个比较通用的配置。

想要更加自定义的配置可以参考代码生成器的配置文档

https://baomidou.com/pages/981406/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.fill.Column;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;


/**
* <p>
* MyBatisPlus的代码生成器
* </p>
*
* @ClassName CodeGenerator
* @Author yww
* @Date 2022/10/8 16:26
*/
public class CodeGenerator {

/**
* 获取配置文件application.yml中关于mysql的配置
* @return 数据库配置
*/
@NotNull
@Contract(" -> new")
private static DataSourceConfig.Builder getDataSourceConfig() {
// 配置文件为application.yml使用
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(new ClassPathResource("application.yml"));
Properties properties = factoryBean.getObject();
if (properties == null) {
throw new RuntimeException("请确认配置文件是否名为application.yml,是否在resources资源目录下!");
}
String url = (String) properties.get("spring.datasource.url");
String username = (String) properties.get("spring.datasource.username");
String password = (String) properties.get("spring.datasource.password");
if (url == null || url.isEmpty()) {
throw new RuntimeException("mysql的url出错!");
}
if (username == null || username.isEmpty()) {
throw new RuntimeException("mysql的username出错!");
}
if (password == null || password.isEmpty()) {
throw new RuntimeException("mysql的password出错!");
}
return new DataSourceConfig.Builder(url, username, password);
}

/**
* 执行 run
*/
public static void main(String[] args) {
// 项目路径 + /src/main
String path = System.getProperty("user.dir").replaceAll("\\\\","/") + "/src/main";
FastAutoGenerator.create(getDataSourceConfig())
// 全局配置
.globalConfig(builder -> builder
// 作者名称
.author("yww")
// 指定输出目录
.outputDir(path + "/java")
// 禁止打开输出目录
.disableOpenDir()
// 时间策略
.dateType(DateType.TIME_PACK)
// 注释日期
.commentDate("yyyy-MM-dd")
// 开启springdoc的注解
.enableSpringdoc()
.build()
)
// 包配置
.packageConfig(builder -> builder
// 父包名
.parent("com.yww.management")
.build()
)
// 模板配置
.templateConfig(builder -> builder
// 禁用所有模板
//.disable()
.controller(path + "/resources/templates/controller.java.vm")
.build())
// 策略配置
.strategyConfig((scanner, builder) -> builder
// 增加表匹配
.addInclude(getTables(scanner.apply("请输入表名,如需输入多个表名,可用英文逗号分隔,选择所有表输入all")))
// controller策略
.controllerBuilder().enableRestStyle().enableHyphenStyle()
// entity策略配置
.entityBuilder().enableLombok()
.idType(IdType.ASSIGN_ID)
.enableTableFieldAnnotation()
.addTableFills(new Column("create_time", FieldFill.INSERT))
.addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE))
.build()
)
.execute();
}

/**
* 处理输入all的情况
*/
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}

}