MyBatis-Plus官方文档笔记

一、特性

  • 无侵入:不影响现有工程
  • 损耗小:启动即会注入基本 CURD,性能基本无损耗
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅通过少量配置即可实现大部分单表 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用
  • 支持主键自动生成:自由配置 4 种主键策略(内含分布式唯一 ID 生成器-Sequence)
  • 支持 ActiveRecord 模式:实体类只需继承 Modal 类即可进行强大的 CRUD 操作
  • 内置代码生成器:采用代码或 Maven 插件可快速生成 Mapper、Modal、Service、Controller 层代码,支持模板引擎
  • 内置分页插件:基于 MyBatis 屋里分页
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句及其执行时间,快速揪出“慢查询”
  • 内置全局拦截插件:提供全表 Delete、Update 操作智能分析阻断,也可自定义拦截规则

二、快速开始

现有一张表 User,其结构如下:

id name age email
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

其对应的数据库 Schema 如下:

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)
);

其对应的数据库 Data 脚本如下:

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');

初始化工程

添加依赖

<project>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</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>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>

添加配置

spring:
  datasource:
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
    schema: classpath:db/schema.sql
    data: classpath:db/data.sql
    url: jdbc:mysql://localhost:3306/mp?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    initialization-mode: always

开启 Mapper 扫描

@SpringBootApplication
@MapperScan("cn.luokaiii.mall.admin.mapper")
public class AdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminApplication.class, args);
    }
}

编码

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

UserMapper.java 中需要加上注解 @Repository,不然 IDEA 会提示无法注入,但实际可运行。

// UserMapper.java
@Repository
public interface UserMapper extends BaseMapper<User> {
}

测试

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

    @Autowired
    private UserMapper userMapper;

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

三、注解

@TableName

表名注解

@TableId

主键注解

@TableField

字段注解(非主键)

@Version

乐观锁注解

@EnumValue

通枚举类注解(注解在枚举字段上)

@TableLogic

表字段逻辑处理注解(逻辑删除)

@SqlParser

@KeySequence

序列主键策略(oracle)

四、代码生成器

mybatis-plus-generator 详见 MyBatis-Plus 代码生成器

五、条件构造器

使用 Wrapper 自定义 SQL,使用方法如下:

// Service.java
mapper.getAll(Wrappers.<User>lambdaQuery().eq(User::getName, "Jack"));

// 注解方式
@Select("select * from user ${ew.customSqlSegment}")
List<User> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);

// XML方式
<select id="getAll" resultType="User">
    SELECT * FROM user ${ew.customSqlSegment}
</select>

Wrapper 的条件包含:allEqeqnegtgeltlebetweennotBetweenlikenotLikelikeLeftlikeRightisNullisNotNullinnotIninSqlnotInSqlgroupByorderByAscorderByDescorderByhavingorandnestedapplylastexistsnotExists

QueryWrapper

继承自 AbstractWrapper,可以通过 new QueryWrapper().lambda() 方法获取 LambdaQueryWrapper。

select

// 例:select("id", "name", "age")
select(String... sqlSelect)
// 例:select(i -> i.getProperty().startsWith("test"))
select(Predicate<TableFieldInfo> predicate)

select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)

UpdateWrapper

继承自 AbstractWrapper,可以通过 new UpdateWrapper().lambda() 方法获取 LambdaUpdateWrapper。

set

// 例:set("name", "zhangsan")
// 设置空值:set("name", "")
// 设置为null:set("name", null)
set(String column, Object val)
set(boolean condition, String column, Object val)

setSql

// 例:setSql("name = 'zhangsan'")
setSql(String sql)

LambdaWrapper

使用 LambdaWrapper,在 QueryWrapper 中是获取 LambdaQueryWrapper;在 UpdateWrapper 中是获取 LambdaUpdateWrapper。

六、分页插件

6.1 配置分页功能

@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作,true调回到首页,false继续请求;默认false
        paginationInterceptor.setOverflow(false);
        // 设置单页限制数量,默认500,-1 不受限制
        paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

6.2 使用

// UserMapper.java
@Repository
public interface UserMapper extends BaseMapper<User> {

    @Select("select * from user ${ew.customSqlSegment}")
    List<User> getAll(Page<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
}
// SampleTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void test() {
        List<User> list = userMapper.getAll(new Page<>(0, 2), Wrappers.<User>lambdaQuery());
        list.forEach(System.out::println);
    }
}

七、Sequence 主键

Sequence 主键,与 @TableId 的 Input 主键生成策略 配合使用。

内置支持:DB2KeyGenerator、H2KeyGenerator、KingbaseKeyGenerator、OracleKeyGenerator、PostgreKeyGenerator。还可以通过实现 IKeyGenerator 接口来扩展。

7.1 配置

方式一:使用配置类

@Bean
public IKeyGenerator keyGenerator() {
    return new H2KeyGenerator();
}

方式二:通过 MybatisPlusPropertiesCustomizer 自定义

@Bean
public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
    return plusProperties -> plusProperties
                .getGlobalConfig()
                .getDbConfig()
                .setKeyGenerator(new H2KeyGenerator());
}

八、自定义 ID 生成器

从 3.3.0 开始,默认使用雪花算法+UUID(不含中划线)

8.1 配置

@Bean
public IdentifierGenerator idGenerator() {
    return new CustomIdGenerator();
}

九、插件扩展

9.1 逻辑删除

逻辑删除,使用 MyBatis-Plus 自带的方法都会附带逻辑删除功能。想查找到,可以手写 XML。

// application.yml
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  #全局逻辑删除字段值 3.3.0开始支持,详情看下面。
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
// 注解表示为逻辑删除字段,支持 Integer、Boolean、LocalDateTime(null/now())
@TableLogic
private Boolean deleted;

注:逻辑删除与删除等价,不应该再被查询出来。如果需要在查询时使用,可以用状态表示。

9.2 通用枚举

声明枚举属性

// 使用 @EnumValue 注解枚举属性
public enum GradeEnum {

    PRIMARY(1, "小学"),  SECONDORY(2, "中学"),  HIGH(3, "高中");

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

    @EnumValue//标记数据库存的值是code
    private final int code;
    private final String descp;
}
// 实现 IEnum 接口
public enum AgeEnum implements IEnum<Integer> {
    ONE(1, "一岁"),
    TWO(2, "二岁"),
    THREE(3, "三岁");

    private int value;
    private String desc;

    @Override
    public Integer getValue() {
        return this.value;
    }
}

使用枚举类型

public class User {
    /**
     * 名字
     * 数据库字段: name varchar(20)
     */
    private String name;

    /**
     * 年龄,IEnum接口的枚举处理
     * 数据库字段:age INT(3)
     */
    private AgeEnum age;


    /**
     * 年级,原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
     * 数据库字段:grade INT(2)
     */
    private GradeEnum grade;
}

配置扫描通用枚举

// application.yml
mybatis-plus:
    # 支持统配符 * 或者 ; 分割
    typeEnumsPackage: com.baomidou.springboot.entity.enums

9.3 字段类型处理器

字段类型处理器,用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值,并通过 TableField 注解快速注入。

@Data
@Accessors(chain = true)
// 注意,必须开启映射注解
@TableName(autoResultMap = true)
public class User {
    private Long id;

    ...

    /**
     * 类型处理器,二选一
     */
    @TableField(typeHandler = JacksonTypeHandler.class)
    // @TableField(typeHandler = FastjsonTypeHandler.class)
    private OtherInfo otherInfo;
}

9.4 自动填充功能

字段必须声明 TableField 注解,由属性 fill 选择对应策略。

public enum FieldFill {
    /**
     * 默认不处理
     */
    DEFAULT,
    /**
     * 插入填充字段
     */
    INSERT,
    /**
     * 更新填充字段
     */
    UPDATE,
    /**
     * 插入和更新填充字段
     */
    INSERT_UPDATE
}

9.5 执行 SQL 分析打印

该插件有性能损耗,不建议生产环境使用。

<!-- pom.xml 引入依赖 -->
<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>版本号</version>
</dependency>

修改 application.yml 文件,修改 jdbc url 和驱动程序类名:

spring:
  datasource:
#    driver-class-name: com.mysql.cj.jdbc.Driver
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    # url: jdbc:mysql://localhost:3306/mp
    url: jdbc:p6spy:mysql://localhost:3306/mp
    ...

该文件可以从官网下载,并稍作修改:

<!-- spy.properties -->
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

9.6 乐观锁插件

1.插件配置

// MybatisPlusConfig.java
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
}

2.注解实体字段

// User.java
public class User {
    ...

    @Version
    private Integer version;
}

3.测试:需要带版本号更新,否则不会触发乐观锁

// SampleTest.java
@Test
public void test5() {
    System.out.println("第一次查询:" + userMapper.selectById(1L));
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    user.setVersion(0);
    int i = userMapper.updateById(user);
    System.out.println("更新后:" + userMapper.selectById(1L));
}

9.7 数据安全保护

1.生成随机 AES 密钥,妥善保管密钥,密钥用来解密 YML 中的加密配置:

@Test
public void test() {
    String randomKey = AES.generateRandomKey();
    String url = AES.encrypt("jdbc:p6spy:mysql://localhost:3306/mp", randomKey);
    String username = AES.encrypt("root", randomKey);
    String pwd = AES.encrypt("password", randomKey);
    System.out.println("随机秘钥为:" + randomKey);
    System.out.println("数据库连接地址为:" + url);
    System.out.println("数据库账号为:" + username);
    System.out.println("数据库密码为:" + pwd);
}

2.替换 YML 配置,以 mpw 开头,紧接加密内容,YML 中的其它配置也可以使用:

// application.yml
spring:
  datasource:
    url: mpw:gon0cDU60gipEpMICPWgUJtGmWxnY1riLGNa7MgmN0GG6rl+8yZYh/LyrdSKyOkBDYgOXWuT6m39aghmRweM0wgzhlbYR08m5iJn/fa+GBv3JKxEy8uLHD4dGoQ3mB7TB1Plr/APTsZD6GIR/RakDme/EaMS5AEnMpFz3EYIV3o=
    username: mpw:9L9cgOvm5jezPKYgI3DqtA==
    password: mpw:fParb8j1C9LPKrGSB6gL3w==

3.项目增加启动参数

// Jar 启动参数( idea 设置 Program arguments , 服务器可以设置为启动环境变量 )
--mpw.key=d1104d7c3b616f0b

9.8 多数据源

dynamic-datasource-spring-boot-starter 是一个基于 springboot 的快速集成多数据源的启动器。

特性

  1. 数据源分组,适用于多种场景:纯粹多库;读写分离;一主多从;混合模式。
  2. 内置敏感参数加密和启动初始化表结构schema数据库database
  3. 提供对 Druid、Mybatis-Plus、P6sy、Jndi 的快速集成。
  4. 简化 Druid 和 HikariCp 配置,提供全局参数配置。
  5. 提供自定义数据源来源接口(默认使用 yml 或 properties 配置)。
  6. 提供项目启动后增减数据源方案。
  7. 提供 Mybatis 环境下的 纯读写分离 方案。
  8. 使用 spel 动态参数解析数据源,如从 session,header 或参数中获取数据源。(多租户架构神器)
  9. 提供多层数据源嵌套切换。(ServiceA >>> ServiceB >>> ServiceC,每个 Service 都是不同的数据源)
  10. 提供 不使用注解 而 使用 正则 或 spel 来切换数据源方案(实验性功能)。
  11. 基于 seata 的分布式事务支持。

使用方法

1.引入依赖

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

2.配置数据源

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
      datasource:
        master:
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
        slave_1:
          url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
        slave_2:
          url: ENC(xxxxx) # 内置加密,使用请查看详细文档
          username: ENC(xxxxx)
          password: ENC(xxxxx)
          driver-class-name: com.mysql.jdbc.Driver
          schema: db/schema.sql # 配置则生效,自动初始化表结构
          data: db/data.sql # 配置则生效,自动初始化数据
          continue-on-error: true # 默认true,初始化失败是否继续
          separator: ";" # sql默认分号分隔符
# 多主多从                      纯粹多库(记得设置primary)                   混合配置
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.使用@DS切换数据源

注:1.没有@Ds注解,使用默认数据源(master);2.方法注解优于类注解

@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");
  }
}

9.9 多租户SQL解析器

多租户技术或称多重租赁技术,是一种软件架构技术,是实现如何在多用户环境下(多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性。

10. 踩坑

  1. MyBatis-Plus 枚举类型无法映射
    1. 将字段类型从 tinyint(1) 改为 tinyint(4)
文章作者: koral
文章链接: http://luokaiii.github.io/2020/06/15/后端/MyBatis/Mybatis-Plus/简介/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自