MyBatis Plus 学习
阅读原文时间:2021年06月04日阅读:1

目录

前言

快速入门

简介

特性

框架结构

代码托管

快速开始

初始化工程

添加依赖

配置

编码

开始使用

小结

安装

Release

Snapshot

配置

核心功能

代码生成器

添加依赖

切换模板引擎

自定义模板引擎

CRUD 接口

Mapper CRUD 接口

Service CRUD 接口

updateBatchById

条件构造器

AbstractWrapper

groupBy

last

QueryWrapper

UpdateWrapper

使用 Wrapper 自定义SQL

分页插件

XML 自定义分页

Sequence主键

如何使用Sequence作为主键,但是实体主键类型是String


前言

最近在学习了MyBatis Plus,认真详细查看了官方文档,确实写得非常清楚,就不专门去写MyBatis Plus入门基础博文,直接将官网知识贴在博客里面。个人非常推荐查看这篇文章

当然,除了这篇文章之后,个人也会总结一些MP启动注入原理分析、AQ支持、几个插件底层原理分析等文章。有兴趣可以看一下哦

这一篇当做是文档+基础哦

版本在3.0+,2.3版本跟这个有小小区别哦!

快速入门

简介

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

愿景

我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。

特性

  • 无侵入:Mybatis-Plus 在 Mybatis 的基础上进行扩展,只做增强不做改变,引入 Mybatis-Plus 不会对您现有的 Mybatis 构架产生任何影响,而且 MP 支持所有 Mybatis 原生的特性
  • 依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring
  • 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作
  • 预防Sql注入:内置Sql注入剥离器,有效预防Sql注入攻击
  • 通用CRUD操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题
  • 支持ActiveRecord:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作
  • 支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(P.S. 比 Mybatis 官方的 Generator 更加强大!)
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 支持关键词自动转义:支持数据库关键词(order、key……)自动转义,还可自定义关键词
  • 内置分页插件:基于Mybatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通List查询
  • 内置性能分析插件:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作

框架结构

代码托管

Gitee | Github

快速开始

我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,我们假设您已经:

  • 拥有 Java 开发环境以及相应 IDE
  • 熟悉 Spring Boot
  • 熟悉 Maven

现有一张 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');

Question

如果从零开始用 MyBatis-Plus 来实现该表的增删改查我们需要做什么呢?

初始化工程

创建一个空的 Spring Boot 工程(工程将以 H2 作为默认数据库进行演示)

TIP

可以使用 Spring Initializr 快速初始化一个 Spring Boot 工程

添加依赖

引入 Spring Boot Starter 父工程:

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

引入 spring-boot-starterspring-boot-starter-testmybatis-plus-boot-starterlombokh2 依赖:

<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.0.6</version>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

配置

在 application.yml 配置文件中添加 H2 数据库的相关配置:

# DataSource Config
spring:
  datasource:
    driver-class-name: org.h2.Driver
    schema: classpath:db/schema-h2.sql
    data: classpath:db/data-h2.sql
    url: jdbc:h2:mem:test
    username: root
    password: test

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:

@SpringBootApplication
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {

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

}

编码

编写实体类 User.java(此处使用了 Lombok 简化代码)

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

编写Mapper类 UserMapper.java

public interface UserMapper extends BaseMapper<User> {

}

开始使用

添加测试类,进行功能测试:

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

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        Assert.assertEquals(5, userList.size());
        userList.forEach(System.out::println);
    }

}

TIP

UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件

控制台输出:

User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)

TIP

完整的代码示例请移步:Spring Boot 快速启动示例 | Spring MVC 快速启动示例

小结

通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!

从以上步骤中,我们可以看到集成MyBatis-Plus非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。

但 MyBatis-Plus 的强大远不止这些功能,想要详细了解 MyBatis-Plus 的强大功能?那就继续往下看吧!

安装

全新的 MyBatis-Plus 3.0 版本基于 JDK8,提供了 lambda 形式的调用,所以安装集成 MP3.0 要求如下:

  • JDK 8+
  • Maven or Gradle

TIP

JDK7 以及下的请参考 MP2.0 版本,地址:2.0 文档

Release

Spring Boot

Maven:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.6</version>
</dependency>

Gradle:

compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.0.6'

Spring MVC

Maven:

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

Gradle:

compile group: 'com.baomidou', name: 'mybatis-plus', version: '3.0.6'

WARNING

引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 MyBatis-Spring,以避免因版本差异导致的问题。

Snapshot

快照 SNAPSHOT 版本需要添加仓库,且版本号为快照版本。

Maven:

<repository>
    <id>snapshots</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>

Gradle:

repositories {
    maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

配置

MyBatis-Plus 的配置异常的简单,我们仅需要一些简单的配置即可使用 MyBatis-Plus 的强大功能!

TIP

在讲解配置之前,请确保您已经安装了 MyBatis-Plus,如果您尚未安装,请查看 安装 一章。

  • Spring Boot 工程:

    • 配置 MapperScan 注解

      @SpringBootApplication
      @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
      public class Application {
      public static void main(String[] args) {
          SpringApplication.run(QuickStartApplication.class, args);
      }
      }
  • Spring MVC 工程:

    • 配置 MapperScan

      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
          <property name="basePackage" value="com.baomidou.mybatisplus.samples.quickstart.mapper"/>
      </bean>
    • 调整 SqlSessionFactory 为 MyBatis-Plus 的 SqlSessionFactory

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

通常来说,一般的简单工程,通过以上配置即可正常使用 MyBatis-Plus,具体可参考以下项目:Spring Boot 快速启动示例Spring MVC 快速启动示例

同时 MyBatis-Plus 提供了大量的个性化配置来满足不同复杂度的工程,大家可根据自己的项目按需取用,详细配置请参考配置一文

核心功能

代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("jobob");
        gc.setOpen(false);
        mpg.setGlobalConfig(gc);

        // 数据源配置
        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("密码");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模块名"));
        pc.setParent("com.baomidou.ant");
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定义输出模板
        // templateConfig.setEntity();
        // templateConfig.setService();
        // templateConfig.setController();

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

        // 策略配置
        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("表名"));
        strategy.setSuperEntityColumns("id");
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}

更多详细配置,请参考代码生成器配置一文。

添加依赖

MP 3.0.3 之后移除了自动模板引擎依赖,需要手动添加对应引擎的依赖坐标如下:

<!-- velocity 模板引擎, 默认 -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>最新版本</version>
</dependency>

<!-- freemarker 模板引擎 -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>最新版本</version>
</dependency>

切换模板引擎

// 切换为 freemarker 模板引擎
mpg.setTemplateEngine(new FreemarkerTemplateEngine());

自定义模板引擎

请继承类 com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine

CRUD 接口

Mapper CRUD 接口

说明:

  • 通用 CRUD 封装BaseMapper接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
  • 泛型 T 为任意实体对象
  • 参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
  • 对象 Wrapper 为 条件构造器

insert

/**
 * <p>
 * 插入一条记录
 * </p>
 *
 * @param entity 实体对象
 * @return 插入成功记录数
 */
int insert(T entity);

deleteById

/**
 * <p>
 * 根据 ID 删除
 * </p>
 *
 * @param id 主键ID
 * @return 删除成功记录数
 */
int deleteById(Serializable id);

deleteByMap

/**
 * <p>
 * 根据 columnMap 条件,删除记录
 * </p>
 *
 * @param columnMap 表字段 map 对象
 * @return 删除成功记录数
 */
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

delete

/**
 * <p>
 * 根据 entity 条件,删除记录
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 * @return 删除成功记录数
 */
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

deleteBatchIds

/**
 * <p>
 * 删除(根据ID 批量删除)
 * </p>
 *
 * @param idList 主键ID列表(不能为 null 以及 empty)
 * @return 删除成功记录数
 */
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

updateById

/**
 * <p>
 * 根据 ID 修改
 * </p>
 *
 * @param entity 实体对象
 * @return 修改成功记录数
 */
int updateById(@Param(Constants.ENTITY) T entity);

update

/**
 * <p>
 * 根据 whereEntity 条件,更新记录
 * </p>
 *
 * @param entity        实体对象 (set 条件值,不能为 null)
 * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
 * @return 修改成功记录数
 */
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

selectById

/**
 * <p>
 * 根据 ID 查询
 * </p>
 *
 * @param id 主键ID
 * @return 实体
 */
T selectById(Serializable id);

selectBatchIds

/**
 * <p>
 * 查询(根据ID 批量查询)
 * </p>
 *
 * @param idList 主键ID列表(不能为 null 以及 empty)
 * @return 实体集合
 */
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

selectByMap

/**
 * <p>
 * 查询(根据 columnMap 条件)
 * </p>
 *
 * @param columnMap 表字段 map 对象
 * @return 实体集合
 */
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

selectOne

/**
 * <p>
 * 根据 entity 条件,查询一条记录
 * </p>
 *
 * @param queryWrapper 实体对象
 * @return 实体
 */
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectCount

/**
 * <p>
 * 根据 Wrapper 条件,查询总记录数
 * </p>
 *
 * @param queryWrapper 实体对象
 * @return 满足条件记录数
 */
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectList

/**
 * <p>
 * 根据 entity 条件,查询全部记录
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 * @return 实体集合
 */
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectMaps

/**
 * <p>
 * 根据 Wrapper 条件,查询全部记录
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 * @return 字段映射对象 Map 集合
 */
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectObjs

/**
 * <p>
 * 根据 Wrapper 条件,查询全部记录
 * 注意: 只返回第一个字段的值
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 * @return 字段映射对象集合
 */
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectPage

/**
 * <p>
 * 根据 entity 条件,查询全部记录(并翻页)
 * </p>
 *
 * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
 * @param queryWrapper 实体对象封装操作类(可以为 null)
 * @return 实体分页对象
 */
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

selectMapsPage

/**
 * <p>
 * 根据 Wrapper 条件,查询全部记录(并翻页)
 * </p>
 *
 * @param page         分页查询条件
 * @param queryWrapper 实体对象封装操作类
 * @return 字段映射对象 Map 分页对象
 */
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

Service CRUD 接口

说明:

  • 通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
  • 对象 Wrapper 为 条件构造器

save

/**
 * <p>
 * 插入一条记录(选择字段,策略插入)
 * </p>
 *
 * @param entity 实体对象
 */
boolean save(T entity);

saveBatch

/**
 * <p>
 * 插入一条记录(选择字段,策略插入)
 * </p>
 *
 * @param entity 实体对象
 */
boolean save(T entity);

saveOrUpdateBatch

/**
 * <p>
 * 批量修改插入
 * </p>
 *
 * @param entityList 实体对象集合
 */
boolean saveOrUpdateBatch(Collection<T> entityList);

saveOrUpdateBatch

/**
 * <p>
 * 批量修改插入
 * </p>
 *
 * @param entityList 实体对象集合
 * @param batchSize  每次的数量
 */
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

removeById

/**
 * <p>
 * 根据 ID 删除
 * </p>
 *
 * @param id 主键ID
 */
boolean removeById(Serializable id);

removeByMap

/**
 * <p>
 * 根据 columnMap 条件,删除记录
 * </p>
 *
 * @param columnMap 表字段 map 对象
 */
boolean removeByMap(Map<String, Object> columnMap);

remove

/**
 * <p>
 * 根据 entity 条件,删除记录
 * </p>
 *
 * @param queryWrapper 实体包装类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
boolean remove(Wrapper<T> queryWrapper);

removeByIds

/**
 * <p>
 * 删除(根据ID 批量删除)
 * </p>
 *
 * @param idList 主键ID列表
 */
boolean removeByIds(Collection<? extends Serializable> idList);

updateById

/**
 * <p>
 * 根据 ID 选择修改
 * </p>
 *
 * @param entity 实体对象
 */
boolean updateById(T entity);

update

/**
 * <p>
 * 根据 whereEntity 条件,更新记录
 * </p>
 *
 * @param entity        实体对象
 * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
 */
boolean update(T entity, Wrapper<T> updateWrapper);

updateBatchById

/**
 * <p>
 * 根据ID 批量更新
 * </p>
 *
 * @param entityList 实体对象集合
 * @param batchSize  更新批次数量
 */
boolean updateBatchById(Collection<T> entityList, int batchSize);

saveOrUpdate

/**
 * <p>
 * TableId 注解存在更新记录,否插入一条记录
 * </p>
 *
 * @param entity 实体对象
 */
boolean saveOrUpdate(T entity);

getById

/**
 * <p>
 * 根据 ID 查询
 * </p>
 *
 * @param id 主键ID
 */
T getById(Serializable id);

listByIds

/**
 * <p>
 * 查询(根据ID 批量查询)
 * </p>
 *
 * @param idList 主键ID列表
 */
Collection<T> listByIds(Collection<? extends Serializable> idList);

listByMap

/**
 * <p>
 * 查询(根据 columnMap 条件)
 * </p>
 *
 * @param columnMap 表字段 map 对象
 */
Collection<T> listByMap(Map<String, Object> columnMap);

getOne

/**
 * <p>
 * 根据 Wrapper,查询一条记录
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 * @param throwEx      有多个 result 是否抛出异常
 */
T getOne(Wrapper<T> queryWrapper, boolean throwEx);

getMap

/**
 * <p>
 * 根据 Wrapper,查询一条记录
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
Map<String, Object> getMap(Wrapper<T> queryWrapper);

getObj

/**
 * <p>
 * 根据 Wrapper,查询一条记录
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
Object getObj(Wrapper<T> queryWrapper);

count

/**
 * <p>
 * 根据 Wrapper 条件,查询总记录数
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
int count(Wrapper<T> queryWrapper);

list

/**
 * <p>
 * 查询列表
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
List<T> list(Wrapper<T> queryWrapper);

page

/**
 * <p>
 * 翻页查询
 * </p>
 *
 * @param page         翻页对象
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);

listMaps

/**
 * <p>
 * 查询列表
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);

listObjs

/**
 * <p>
 * 根据 Wrapper 条件,查询全部记录
 * </p>
 *
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
List<Object> listObjs(Wrapper<T> queryWrapper);

pageMaps

/**
 * <p>
 * 翻页查询
 * </p>
 *
 * @param page         翻页对象
 * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
 */
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

条件构造器

说明:

  • 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中
  • 以下代码块内的多个方法均为从上往下补全个别boolean类型的入参,默认为true
  • 以下出现的泛型This均为具体使用的Wrapper的实例
  • 以下方法在入参中出现的R为泛型,在普通wrapper中是String,在LambdaWrapper中是函数(例:Entity::getId,Entity为实体类,getId为字段idgetMethod)
  • 以下方法入参中的R column均表示数据库字段,当RString时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹!)!而不是实体类数据字段名!!!
  • 以下举例均为使用普通wrapper,入参为MapList的均以json形式表现!
  • 使用中如果入参的Map或者List,则不会加入最后生成的sql中!!!
  • 有任何疑问就点开源码看,看不懂函数点击我学习新知识

AbstractWrapper

说明:

QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

allEq

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

个别参数说明:

params : key为数据库字段名,value为字段值
null2IsNull : 为true则在mapvaluenull时调用 isNull 方法,为false时则忽略valuenull

  • 例1: allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null

  • 例2: allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'

    allEq(BiPredicate filter, Map params)
    allEq(BiPredicate filter, Map params, boolean null2IsNull)
    allEq(boolean condition, BiPredicate filter, Map params, boolean null2IsNull)

个别参数说明:

filter : 过滤函数,是否允许字段传入比对条件中
params 与 null2IsNull : 同上

  • 例1: allEq((k,v) -> k.indexOf("a") > 0, {id:1,name:"老王",age:null})--->name = '老王' and age is null
  • 例2: allEq((k,v) -> k.indexOf("a") > 0, {id:1,name:"老王",age:null}, false)--->name = '老王'

eq

eq(R column, Object val)
eq(boolean condition, R column, Object val)
  • 等于 =
  • 例: eq("name", "老王")--->name = '老王'

ne

ne(R column, Object val)
ne(boolean condition, R column, Object val)
  • 不等于 <>
  • 例: ne("name", "老王")--->name <> '老王'

gt

gt(R column, Object val)
gt(boolean condition, R column, Object val)
  • 大于 >
  • 例: gt("age", 18)--->age > 18

ge

ge(R column, Object val)
ge(boolean condition, R column, Object val)
  • 大于等于 >=
  • 例: ge("age", 18)--->age >= 18

lt

lt(R column, Object val)
lt(boolean condition, R column, Object val)
  • 小于 <
  • 例: lt("age", 18)--->age < 18

le

le(R column, Object val)
le(boolean condition, R column, Object val)
  • 小于等于 <=
  • 例: le("age", 18)--->age <= 18

between

between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)
  • BETWEEN 值1 AND 值2
  • 例: 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 值1 AND 值2
  • 例: notBetween("age", 18, 30)--->age not between 18 and 30

like

like(R column, Object val)
like(boolean condition, R column, Object val)
  • LIKE '%值%'
  • 例: like("name", "王")--->name like '%王%'

notLike

notLike(R column, Object val)
notLike(boolean condition, R column, Object val)
  • NOT LIKE '%值%'
  • 例: notLike("name", "王")--->name not like '%王%'

likeLeft

likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)
  • LIKE '%值'
  • 例: likeLeft("name", "王")--->name like '%王'

likeRight

likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)
  • LIKE '值%'
  • 例: likeRight("name", "王")--->name like '王%'

isNull

isNull(R column)
isNull(boolean condition, R column)
  • 字段 IS NULL
  • 例: isNull("name")--->name is null

isNotNull

isNotNull(R column)
isNotNull(boolean condition, R column)
  • 字段 IS NULL
  • 例: isNotNull("name")--->name is not null

in

in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)
  • 字段 IN (value.get(0), value.get(1), …)

  • 例: in("age",{1,2,3})--->age in (1,2,3)

    in(R column, Object… values)
    in(boolean condition, R column, Object… values)

  • 字段 IN (v0, v1, …)

  • 例: in("age", 1, 2, 3)--->age in (1,2,3)

notIn

notIn(R column, Collection<?> value)
notIn(boolean condition, R column, Collection<?> value)
  • 字段 IN (value.get(0), value.get(1), …)

  • 例: notIn("age",{1,2,3})--->age not in (1,2,3)

    notIn(R column, Object… values)
    notIn(boolean condition, R column, Object… values)

  • 字段 NOT IN (v0, v1, …)

  • 例: notIn("age", 1, 2, 3)--->age not in (1,2,3)

inSql

inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
  • 字段 IN ( sql语句 )
  • 例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
  • 例: 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)
  • 字段 NOT IN ( sql语句 )
  • 例: notInSql("age", "1,2,3,4,5,6")--->age not in (1,2,3,4,5,6)
  • 例: 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)
  • 分组:GROUP BY 字段, …
  • 例: groupBy("id", "name")--->group by id,name

orderByAsc

orderByAsc(R... columns)
orderByAsc(boolean condition, R... columns)
  • 排序:ORDER BY 字段, … ASC
  • 例: orderByAsc("id", "name")--->order by id ASC,name ASC

orderByDesc

orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
  • 排序:ORDER BY 字段, … DESC
  • 例: orderByDesc("id", "name")--->order by id DESC,name DESC

orderBy

orderBy(boolean condition, boolean isAsc, R... columns)
  • 排序:ORDER BY 字段, …
  • 例: 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语句 )
  • 例: having("sum(age) > 10")--->having sum(age) > 10
  • 例: having("sum(age) > {0}", 11)--->having sum(age) > 11

or

or()
or(boolean condition)
  • 拼接 OR

注意事项:

主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)

  • 例: eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'

    or(Function func)
    or(boolean condition, Function func)

  • OR 嵌套

  • 例: or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')

and

and(Function<This, This> func)
and(boolean condition, Function<This, This> func)
  • AND 嵌套
  • 例: and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')

apply

apply(String sqlHaving, Object... params)
apply(boolean condition, String sqlHaving, Object... params)
  • 拼接 sql

注意事项:

该方法可用于数据库函数 动态入参的params对应前面sqlHaving内部的{index}部分.这样是不会有sql注入风险的,反之会有!

  • 例: apply("id = 1")--->having sum(age) > 10
  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
  • 例: 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)
  • 无视优化规则直接拼接到 sql 的最后

注意事项:

只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

  • 例: last("limit 1")

exists

exists(String existsSql)
exists(boolean condition, String existsSql)
  • 拼接 EXISTS ( sql语句 )
  • 例: exists("select id from table where age = 1")--->exists (select id from table where age = 1)

notExists

notExists(String existsSql)
notExists(boolean condition, String existsSql)
  • 拼接 NOT EXISTS ( sql语句 )
  • 例: notExists("select id from table where age = 1")--->not exists (select id from table where age = 1)

nested

nested(Function<This, This> func)
nested(boolean condition, Function<This, This> func)
  • 正常嵌套 不带 AND 或者 OR
  • 例: nested(i -> i.eq("name", "李白").ne("status", "活着"))--->(name = '李白' and status <> '活着')

QueryWrapper

说明:

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取

select

select(String... sqlSelect)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
  • 设置查询字段

说明:

以上方分法为两类.
第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper内的entity属性有值! 这两类方法重复调用以最后一次为准

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

excludeColumns @Deprecated

  • 排除查询字段

WARNING

已从3.0.5版本上移除此方法!

UpdateWrapper

说明:

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

set

set(String column, Object val)
set(boolean condition, String column, Object val)
  • SQL SET 字段
  • 例: set("name", "老李头")
  • 例: set("name", "")--->数据库字段值变为空字符串
  • 例: set("name", null)--->数据库字段值变为null

setSql

setSql(String sql)
  • 设置 SET 部分 SQL
  • 例: set("name = '老李头')

lambda

  • 获取 LambdaWrapper
    QueryWrapper中是获取LambdaQueryWrapper
    UpdateWrapper中是获取LambdaUpdateWrapper

使用 Wrapper 自定义SQL

需求来源:

在使用了mybatis-plus之后, 自定义SQL的同时也想使用Wrapper的便利应该怎么办? 在mybatis-plus版本3.0.7得到了完美解决 版本需要大于或等于3.0.7, 以下两种方案取其一即可

Service.java

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

方案一 注解方式 Mapper.java

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

方案二 XML形式 Mapper.xml

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

分页插件

示例工程:

mybatis-plus-sample-pagination

<!-- spring xml 方式 -->
<plugins>
    <plugin interceptor="com.baomidou.mybatisplus.plugins.PaginationInterceptor">
        <property name="sqlParser" ref="自定义解析类、可以没有" />
        <property name="dialectClazz" value="自定义方言类、可以没有" />
    </plugin>
</plugins>


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

    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

XML 自定义分页

  • UserMapper.java 方法内容

    public interface UserMapper{//可以继承或者不继承BaseMapper
    /**
    *

    * 查询 : 根据state状态查询用户列表,分页显示 * 注意!!: 如果入参是有多个,需要加注解指定参数名才能在xml中取值 *


    *
    * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象)
    * @param state 状态
    * @return 分页对象
    */
    IPage selectPageVo(Page page, @Param("state") Integer state);
    }

  • UserMapper.xml 等同于编写一个普通 list 查询,mybatis-plus 自动替你分页

  • UserServiceImpl.java 调用分页方法

    public IPage selectUserPage(Page page, Integer state) {
    // 不进行 count sql 优化,解决 MP 无法自动优化 SQL 问题,这时候你需要自己查询 count 部分
    // page.setOptimizeCountSql(false);
    // 当 total 为非 0 时(默认为 0),分页插件不会进行 count 查询
    // 要点!! 分页返回的对象与传入的对象是同一个
    return userMapper.selectPageVo(page, state));
    }

Sequence主键

实体主键支持Sequence

  • oracle等数据库主键策略配置Sequence

  • GlobalConfiguration配置KeyGenerator

    GlobalConfiguration gc = new GlobalConfiguration();
    gc.setKeyGenerator(new OracleKeyGenerator());

  • mybatis-plus-boot-starter配置参考

    @Bean
    public OracleKeyGenerator oracleKeyGenerator(){
    return new OracleKeyGenerator();
    }

  • 实体类配置主键Sequence,指定主键@TableId(type=IdType.INPUT)//不能使用AUTO

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

    }

  • 支持父类定义@KeySequence, 子类使用,这样就可以几个表共用一个Sequence

    @KeySequence("SEQ_TEST")
    public abstract class Parent{

    }

    public class Child extends Parent{

    }

以上步骤就可以使用Sequence当主键了。

Spring MVC:xml配置,请参考【安装集成

如何使用Sequence作为主键,但是实体主键类型是String

也就是说,表的主键是varchar2, 但是需要从sequence中取值

  • 1.实体定义@KeySequence 注解clazz指定类型String.class

  • 2.实体定义主键的类型String

    @KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)
    public class YourEntity{

    @TableId(value = "ID_STR", type = IdType.INPUT)
    private String idStr;
    ...

    }

  • 3.正常配置GlobalConfiguration.keyGenerator

    @Bean
    public GlobalConfiguration globalConfiguration() {
    GlobalConfiguration conf = new GlobalConfiguration();
    conf.setKeyGenerator(new OracleKeyGenerator());
    return conf;
    }

关于MybatisPlus插件,详细见下一篇。
https://blog.csdn.net/xiaozhegaa/article/details/85042299