mybatis-plus学习

mybatis-plus
一、简介

官网:https://mp.baomidou.com/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-155EWNFK-1613658092306)(…/image/image-20200719110124543.png)]

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

特性

无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可*配置,完美解决主键问题

支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )

内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用

内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询

分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库

内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询

内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

框架结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vbtzbEqo-1613658092309)(…/image/image-20200719110607940.png)]

二、快速入门

快速开始:https://mp.baomidou.com/guide/quick-start.html

初始化工程

初始化springboot 工程项目

导入依赖

引入父工程:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

添加依赖:

  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <!-- 引入阿里数据库连接池 -->
  <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
  </dependency>
  <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>
  <!-- mybatisPlus 核心库 -->
  <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
       <version>3.0.5</version>
  </dependency>

初始化数据库

创建数据库:

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

配置

application.yml

server:
  port: 9091

spring:
  application:
    name: mybatis-plus-demo
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mybatis-plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.jdbc.Driver

# 打印sql语句在控制台
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

实体entity:

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

mapper:

public interface UserMapper extends BaseMapper<User> {
}

启动类配置扫描 @MapperScan:

@SpringBootApplication
@MapperScan("com.zengqingfa.examples.mybatisplus.mapper")
public class MyBatisPlusApplication {

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

测试:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyBatisPlusApplication.class)
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);
    }
}

结果:

==>  Preparing: SELECT id,name,age,email FROM user 
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==      Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5d497a91]
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)
三、注解

包目录:com.baomidou.mybatisplus.annotation

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BsVnM641-1613658092311)(…/image/image-20200802090723505.png)]

@TableName
  • 描述:表名注解
属性 类型 必须指定 默认值 描述
value String “” 表名
schema String “” schema
keepGlobalPrefix boolean false 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)
resultMap String “” xml 中 resultMap 的 id
autoResultMap boolean false 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入)
@TableId
  • 描述:主键注解
属性 类型 必须指定 默认值 描述
value String “” 主键字段名
type Enum IdType.NONE 主键类型
IdType
描述
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)
@TableField
  • 描述:字段注解(非主键)
属性 类型 必须指定 默认值 描述
value String “” 数据库字段名
el String “” 映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... } 部分
exist boolean true 是否为数据库表字段
condition String “” 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考
update String “” 字段 update set 部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于 el 属性)
insertStrategy Enum N DEFAULT 举例:NOT_NULL: insert into table_a(column) values (#{columnProperty})
updateStrategy Enum N DEFAULT 举例:IGNORED: update table_a set column=#{columnProperty}
whereStrategy Enum N DEFAULT 举例:NOT_EMPTY: where column=#{columnProperty}
fill Enum FieldFill.DEFAULT 字段自动填充策略
select boolean true 是否进行 select 查询
keepGlobalFormat boolean false 是否保持使用全局的 format 进行处理
jdbcType JdbcType JdbcType.UNDEFINED JDBC类型 (该默认值不代表会按照该值生效)
typeHandler Class<? extends TypeHandler> UnknownTypeHandler.class 类型处理器 (该默认值不代表会按照该值生效)
numericScale String “” 指定小数点后保留的位数

关于jdbcTypetypeHandler以及numericScale的说明:

numericScale只生效于 update 的sql. jdbcTypetypeHandler如果不配合@TableName#autoResultMap = true一起使用,也只生效于 update 的sql. 对于typeHandler如果你的字段类型和set进去的类型为equals关系,则只需要让你的typeHandler让Mybatis加载到即可,不需要使用注解

FieldStrategy
描述
IGNORED 忽略判断
NOT_NULL 非NULL判断
NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
DEFAULT 追随全局配置
FieldFill
描述
DEFAULT 默认不处理
INSERT 插入时填充字段
UPDATE 更新时填充字段
INSERT_UPDATE 插入和更新时填充字段
@Version
  • 描述:乐观锁注解、标记 @Verison 在字段上
@EnumValue
  • 描述:通枚举类注解(注解在枚举字段上)
@TableLogic
  • 描述:表字段逻辑处理注解(逻辑删除)
属性 类型 必须指定 默认值 描述
value String “” 逻辑未删除值
delval String “” 逻辑删除值
@SqlParser
  • 描述:租户注解,支持method上以及mapper接口上
属性 类型 必须指定 默认值 描述
filter boolean false true: 表示过滤SQL解析,即不会进入ISqlParser解析链,否则会进解析链并追加例如tenant_id等条件
@KeySequence
  • 描述:序列主键策略 oracle
  • 属性:value、resultMap
属性 类型 必须指定 默认值 描述
value String “” 序列名
clazz Class Long.class id的类型, 可以指定String.class,这样返回的Sequence值是字符串"1"
四、主键生成策略
public enum IdType {
    AUTO(0),  //数据库自增
    NONE(1),  //未设置主键
    INPUT(2), //手动输入
    ID_WORKER(3),//默认
    UUID(4),//uuid
    ID_WORKER_STR(5);//字符串表示法

    private int key;

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

    public int getKey() {
        return this.key;
    }
}

默认策略:雪花算法 ID_WORKER

实体未配置策略:

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

数据库未配置自增长:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v4acS0GC-1613658092313)(…/image/image-20200802102953372.png)]

测试插入:

  @Test
    public void testInsert() {
        User user = new User();
        user.setAge(31);
        user.setEmail("zengqingfa_java@163.com");
        user.setName("zengqingfa2");
        userMapper.insert(user);
    }

sql:

==>  Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) 
==> Parameters: 1289746797000507394(Long), zengqingfa2(String), 31(Integer), zengqingfa_java@163.com(String)
<==    Updates: 1

再次插入:

==>  Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) 
==> Parameters: 1289750439589912578(Long), zengqingfa3(String), 31(Integer), zengqingfa_java@163.com(String)
<==    Updates: 1

源码分析:GlobConfig

public class GlobalConfig implements Serializable {

    /**
     * 是否开启 LOGO
     */
    private boolean banner = true;
    /**
     * 是否刷新 mapper
     */
    private boolean refresh = false;
    /**
     * 缓存 Sql 解析初始化
     */
    private boolean sqlParserCache = false;
    /**
     * 机器 ID 部分
     */
    private Long workerId;
    /**
     * 数据标识 ID 部分
     */
    private Long datacenterId;
    /**
     * 数据库相关配置
     */
    private DbConfig dbConfig;
    /**
     * SQL注入器
     */
    private ISqlInjector sqlInjector;
    /**
     * 缓存当前Configuration的SqlSessionFactory
     */
    @Setter(value = AccessLevel.NONE)
    private SqlSessionFactory sqlSessionFactory;
    /**
     * 缓存已注入CRUD的Mapper信息
     */
    private Set<String> mapperRegistryCache = new ConcurrentSkipListSet<>();
    /**
     * 元对象字段填充控制器
     */
    private MetaObjectHandler metaObjectHandler;

    /**
     * <p>
     * 标记全局设置 (统一所有入口)
     * </p>
     */
    public void signGlobalConfig(SqlSessionFactory sqlSessionFactory) {
        if (null != sqlSessionFactory) {
            GlobalConfigUtils.setGlobalConfig(sqlSessionFactory.getConfiguration(), this);
        }
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Data
    public static class DbConfig {

        /**
         * 数据库类型
         */
        private DbType dbType = DbType.OTHER;
        /**
         * 主键类型(默认 ID_WORKER)
         */
        private IdType idType = IdType.ID_WORKER;
        /**
         * 表名前缀
         */
        private String tablePrefix;
        /**
         * 表名、是否使用下划线命名(默认 true:默认数据库表下划线命名)
         */
        private boolean tableUnderline = true;
        /**
         * String 类型字段 LIKE
         */
        private boolean columnLike = false;
        /**
         * 大写命名
         */
        private boolean capitalMode = false;
        /**
         * 表关键词 key 生成器
         */
        private IKeyGenerator keyGenerator;
        /**
         * 逻辑删除全局值(默认 1、表示已删除)
         */
        private String logicDeleteValue = "1";
        /**
         * 逻辑未删除全局值(默认 0、表示未删除)
         */
        private String logicNotDeleteValue = "0";
        /**
         * 字段验证策略
         */
        private FieldStrategy fieldStrategy = FieldStrategy.NOT_NULL;
    }
}

配置数据中心id和机器id

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #配置xml文件位置
  mapper-locations: classpath:/mapper/**.xml
  #配置逻辑删除
  global-config:
    datacenter-id: 0 #设置中心id
    worker-id: 1

配置自增策略

实体增加自增策略:

   @TableId(type = IdType.AUTO)
    private Long id;

数据库配置:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JXAot5SO-1613658092315)(…/image/image-20200802103719379.png)]

效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q6bXvxer-1613658092317)(…/image/image-20200802103739412.png)]

五、CRUD接口
Service CRUD 接口

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

Save
// 插入一条记录(选择字段,策略插入)
boolean save(T entity);
// 插入(批量)
boolean saveBatch(Collection<T> entityList);
// 插入(批量)
boolean saveBatch(Collection<T> entityList, int batchSize);

参数说明

类型 参数名 描述
T entity 实体对象
Collection entityList 实体对象集合
int batchSize 插入批次数量
SaveOrUpdate
// TableId 注解存在更新记录,否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList);
// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

参数说明

类型 参数名 描述
T entity 实体对象
Wrapper updateWrapper 实体对象封装操作类 UpdateWrapper
Collection entityList 实体对象集合
int batchSize 插入批次数量
Remove
// 根据 entity 条件,删除记录
boolean remove(Wrapper<T> queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件,删除记录
boolean removeByMap(Map<String, Object> columnMap);
// 删除(根据ID 批量删除)
boolean removeByIds(Collection<? extends Serializable> idList);

参数说明

类型 参数名 描述
Wrapper queryWrapper 实体包装类 QueryWrapper
Serializable id 主键ID
Map<String, Object> columnMap 表字段 map 对象
Collection<? extends Serializable> idList 主键ID列表
Update
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 whereEntity 条件,更新记录
boolean update(T entity, Wrapper<T> updateWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);

参数说明

类型 参数名 描述
Wrapper updateWrapper 实体对象封装操作类 UpdateWrapper
T entity 实体对象
Collection entityList 实体对象集合
int batchSize 更新批次数量
Get
// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// 根据 Wrapper,查询一条记录
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// 根据 Wrapper,查询一条记录
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

参数说明

类型 参数名 描述
Serializable id 主键ID
Wrapper queryWrapper 实体对象封装操作类 QueryWrapper
boolean throwEx 有多个 result 是否抛出异常
T entity 实体对象
Function<? super Object, V> mapper 转换函数
List
// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 查询全部记录
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

参数说明

类型 参数名 描述
Wrapper queryWrapper 实体对象封装操作类 QueryWrapper
Collection<? extends Serializable> idList 主键ID列表
Map<?String, Object> columnMap 表字段 map 对象
Function<? super Object, V> mapper 转换函数
Page
// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

参数说明

类型 参数名 描述
IPage page 翻页对象
Wrapper queryWrapper 实体对象封装操作类 QueryWrapper
Count
// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);

参数说明

类型 参数名 描述
Wrapper queryWrapper 实体对象封装操作类 QueryWrapper

Chain

query
// 链式查询 普通
QueryChainWrapper<T> query();
// 链式查询 lambda 式。注意:不支持 Kotlin
LambdaQueryChainWrapper<T> lambdaQuery(); 

// 示例:
query().eq("column", value).one();
lambdaQuery().eq(Entity::getId, value).list();
update
// 链式更改 普通
UpdateChainWrapper<T> update();
// 链式更改 lambda 式。注意:不支持 Kotlin 
LambdaUpdateChainWrapper<T> lambdaUpdate();

// 示例:
update().eq("column", value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);
Mapper CRUD 接口

说明:

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

参数说明

类型 参数名 描述
T entity 实体对象
Delete
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

参数说明

类型 参数名 描述
Wrapper wrapper 实体对象封装操作类(可以为 null)
Collection<? extends Serializable> idList 主键ID列表(不能为 null 以及 empty)
Serializable id 主键ID
Map<String, Object> columnMap 表字段 map 对象
Update
// 根据 whereEntity 条件,更新记录
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);

参数说明

类型 参数名 描述
T entity 实体对象 (set 条件值,可为 null)
Wrapper updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
Select
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

参数说明

类型 参数名 描述
Serializable id 主键ID
Wrapper queryWrapper 实体对象封装操作类(可以为 null)
Collection<? extends Serializable> idList 主键ID列表(不能为 null 以及 empty)
Map<String, Object> columnMap 表字段 map 对象
IPage page 分页查询条件(可以为 RowBounds.DEFAULT
六、条件构造器

参考官方文档:

https://mp.baomidou.com/guide/wrapper.html#abstractwrapper

例如:相等

eq
eq(R column, Object val)
eq(boolean condition, R column, Object val)
  • 等于 =
  • 例: eq("name", "老王")—>name = '老王'
like
like(R column, Object val)
like(boolean condition, R column, Object val)
  • LIKE ‘%值%’
  • 例: like("name", "王")—>name like '%王%'
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)
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)
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
七、分页插件
配置分页插件
@Configuration
@EnableTransactionManagement
@MapperScan("com.zengqingfa.examples.mybatisplus.mapper")
public class MyBatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

测试自动分页:

  /**
     * 测试分页
     */
    @Test
    public void testPage() {
        IPage<User> page = new Page<>(1, 3);
        IPage<User> selectPage = userMapper.selectPage(page, null);
        selectPage.getRecords().forEach(System.out::println);

    }

结果:

==>  Preparing: SELECT COUNT(1) FROM user 
==> Parameters: 
<==    Columns: COUNT(1)
<==        Row: 12
==>  Preparing: SELECT id,name,age,email FROM user LIMIT 0,3 
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==      Total: 3
XML 自定义分页/自定义动态条件分页查询

添加mapper文件位置

mybatis-plus:
  mapper-locations: classpath:/mapper/**.xml

定义接口

public interface UserMapper extends BaseMapper<User> {

    /**
     * 分页查询1
     *
     * @return
     */
    IPage<User> selectUserPage1(Page<User> page, @Param("name") String name);

    /**
     * 分页查询2
     *
     * @return
     */
    IPage<User> selectUserPage2(Page<User> page, @Param("user") User user);
}

定义xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zengqingfa.examples.mybatisplus.mapper.UserMapper">

    <!--普通的分页查询-->
    <select id="selectUserPage1" resultType="com.zengqingfa.examples.mybatisplus.entity.User">
        select * from user where name like concat('%',#{name},'%')
    </select>

    <!--动态拼接条件-->
    <select id="selectUserPage2" resultType="com.zengqingfa.examples.mybatisplus.entity.User">
        select * from user
        where 1 = 1
        <if test="user.name !=null and user.name != ''">
            and `name` like concat('%',#{user.name},'%')
        </if>
        <if test="user.age !=null">
            and `age` =#{user.age}
        </if>
        <if test="user.email !=null and user.email != ''">
            and `email` like concat('%',#{user.email},'%')
        </if>
    </select>


</mapper>

测试1

    /**
     * 测试自定义分页1
     */
    @Test
    public void testPage1() {
        Page<User> page = new Page<>(1, 3);
        IPage<User> selectPage = userMapper.selectUserPage1(page, "zengqingfa");
        selectPage.getRecords().forEach(System.out::println);
    }

结果:

 JsqlParserCountOptimize sql=select * from user where name like concat('%',?,'%')
==>  Preparing: SELECT COUNT(1) FROM user WHERE name LIKE concat('%', ?, '%') 
==> Parameters: zengqingfa(String)
<==    Columns: COUNT(1)
<==        Row: 7
==>  Preparing: select * from user where name like concat('%',?,'%') LIMIT 0,3 
==> Parameters: zengqingfa(String)
<==    Columns: id, name, age, email
<==        Row: 1289746074544197634, zengqingfa, 30, zengqingfa_java@163.com
<==        Row: 1289746797000507394, zengqingfa2, 31, zengqingfa_java@163.com
<==        Row: 1289750439589912578, zengqingfa3, 31, zengqingfa_java@163.com
<==      Total: 3

测试2

  /**
     * 测试自定义分页2
     */
    @Test
    public void testPage2() {
        Page<User> page = new Page<>(1, 3);
        User user = new User();
        user.setName("zengqingfa");
        IPage<User> selectPage = userMapper.selectUserPage2(page, user);
        selectPage.getRecords().forEach(System.out::println);
    }

结果:

 JsqlParserCountOptimize sql=select * from user
        where 1 = 1
         
            and `name` like concat('%',?,'%')
==>  Preparing: SELECT COUNT(1) FROM user WHERE 1 = 1 AND `name` LIKE concat('%', ?, '%') 
==> Parameters: zengqingfa(String)
<==    Columns: COUNT(1)
<==        Row: 7
==>  Preparing: select * from user where 1 = 1 and `name` like concat('%',?,'%') LIMIT 0,3 
==> Parameters: zengqingfa(String)
<==    Columns: id, name, age, email
<==        Row: 1289746074544197634, zengqingfa, 30, zengqingfa_java@163.com
<==        Row: 1289746797000507394, zengqingfa2, 31, zengqingfa_java@163.com
<==        Row: 1289750439589912578, zengqingfa3, 31, zengqingfa_java@163.com
<==      Total: 3
八、逻辑删除

说明:

只对自动注入的sql起效:

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 删除: 转变为 更新

例如:

  • 删除: update user set deleted=1 where id = 1 and deleted=0
  • 查找: select id,name,deleted from user where deleted=0

字段类型支持说明:

  • 支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
  • 如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()

附录:

  • 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
  • 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
使用方法

数据库增加字段 delete_flag

添加默认值:0未删除

配置

# 打印sql语句在控制台
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#配置xml文件位置
  mapper-locations: classpath:/mapper/**.xml
#配置逻辑删除
  global-config:
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0

字段添加注解

    @TableLogic
    private Integer deleteFlag;

添加插件

 /**
     * 逻辑删除
     *
     * @return
     */
    @Bean
    public ISqlInjector logicSqlInjector() {
        return new LogicSqlInjector();
    }

测试

通过id删除:

 @Test
 public void testDeleteById(){
        int i = userMapper.deleteById(2L);
        System.out.println(i);
  }
==>  Preparing: UPDATE user SET delete_flag=1 WHERE id=? AND delete_flag=0 
==> Parameters: 2(Long)
<==    Updates: 0

通过id查询:

    @Test
    public void testSelectById(){
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }
==>  Preparing: SELECT id,name,age,email,delete_flag FROM user WHERE id=? AND delete_flag=0 
==> Parameters: 1(Long)
<==    Columns: id, name, age, email, delete_flag
<==        Row: 1, Jone, 18, test1@baomidou.com, 0
<==      Total: 1
常见问题
1. 如何 insert ?
  1. 字段在数据库定义默认值(推荐)
  2. insert 前自己 set 值
  3. 使用自动填充功能
2. 删除接口自动填充功能失效
  1. 使用 update 方法并: UpdateWrapper.set(column, value)(推荐)
  2. 使用 update 方法并: UpdateWrapper.setSql("column=value")
  3. 使用Sql注入器注入com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill并使用(推荐)

删除接口自动填充功能失效

测试:

 @Test
    public void testDeleteById() {
        int i = userMapper.deleteById(3333L);
        System.out.println(i);
    }

sql:

==>  Preparing: UPDATE user SET delete_flag=1 WHERE id=? AND delete_flag=0 
==> Parameters: 3333(Long)
<==    Updates: 1

此时更新人并未更新:

解决

测试:

  @Test
    public void testUpdateById2() {
        User user = new User();
        UpdateWrapper<User> updateWrapper = new UpdateWrapper();
        updateWrapper.set("delete_flag", 1);
        updateWrapper.eq("id",3333L);
        int update = userMapper.update(user, updateWrapper);
        System.out.println(update);
    }

结果:

==>  Preparing: UPDATE user SET update_user_code=?, delete_flag=? WHERE delete_flag=0 AND id = ? 
==> Parameters: zqf_update(String), 1(Integer), 3333(Long)
<==    Updates: 1
九、自动填充功能

创建时间、修改时间、创建人、更新人!这些个操作一遍都是自动化完成的,我们不希望手动更新!

数据库添加字段:

create_time ,update_time,create_user_code,update_user_code,

方式一:数据库级别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xVZ5lZHD-1613658092319)(…/image/image-20200802153233753.png)]

CURRENT_TIMESTAMP

测试:

数据库更新有变化

方式二:代码级别

测试更新人、创建人

注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置!

   @TableField(fill = FieldFill.INSERT)
    private String createUserCode;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateUserCode;

实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 插入值
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createUserCode", "zqf_create", metaObject);
    }

    /**
     * 插入和更新时插入值
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateUserCode", "zqf_update", metaObject);
    }
}

测试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ZX5qLeA-1613658092320)(…/image/image-20200802160910245.png)]

十、乐观锁

乐观锁 : 故名思意十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,

再次更新值测试

悲观锁:故名思意十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作!

意图

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败
使用方法

1、数据库增加字段version

2、实体类增加version,并增加注解 @Version

  	@Version
    private Integer version;

特别说明:

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity
  • 仅支持 updateById(id)update(entity, wrapper) 方法
  • update(entity, wrapper) 方法下, wrapper 不能复用!!!

3、注册插件

	/**
     * 乐观锁插件
     * @return
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
测试
    @Test
    public void testOptimisticLocker() {
        // 1、查询用户信息
        User user = userMapper.selectById(1L);
        // 2、修改用户信息
        user.setName("zengqingfa");
        user.setEmail("zengqingfa@163.com");
        // 3、执行更新操作
        userMapper.updateById(user);
    }

sql:

==>  Preparing: SELECT id,name,age,email,delete_flag,create_time,update_time,create_user_code,update_user_code,version FROM user WHERE id=? AND delete_flag=0 
==> Parameters: 1(Long)
<==    Columns: id, name, age, email, delete_flag, create_time, update_time, create_user_code, update_user_code, version
<==        Row: 1, Jone, 18, test1@baomidou.com, 0, null, 2020-08-02 16:51:44.0, null, , 0
<==      Total: 1
==>  Preparing: UPDATE user SET name=?, age=?, email=?, update_time=?, update_user_code=?, version=? WHERE id=? AND version=? AND delete_flag=0 
==> Parameters: zengqingfa(String), 18(Integer), zengqingfa@163.com(String), 2020-08-02 16:51:44.0(Timestamp), zqf_update(String), 1(Integer), 1(Long), 0(Integer)
<==    Updates: 1
示例SQL原理
update tbl_user set name = 'update',version = 3 where id = 100 and version = 2
十一、性能分析插件

作用:性能分析拦截器,用于输出每条 SQL 语句及其执行时间

MP也提供性能分析插件,如果超过这个时间就停止运行!

使用方法

导入插件

    @Bean
    @Profile({"dev","test"})//设置dev.test环境开启
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(30);
        //是否格式化代码
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    }

设置环境

spring:
  profiles:
    active: dev

测试

    @Test
    public void testPerformance2() {
        // 1、查询用户信息
        List<User> users = userMapper.selectList(null);
        System.out.println(users);
    }

超过30ms失败:

Time:33 ms - ID:com.zengqingfa.examples.mybatisplus.mapper.UserMapper.selectList
Execute SQL:
    SELECT
        id,
        name,
        age,
        email,
        delete_flag,
        create_time,
        update_time,
        create_user_code,
        update_user_code,
        version 
    FROM
        user 
    WHERE
        delete_flag=0

Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75a0c890]

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/zengqingfa/examples/mybatisplus/mapper/UserMapper.java (best guess)
### The error may involve com.zengqingfa.examples.mybatisplus.mapper.UserMapper.selectList
### The error occurred while handling results
### SQL: SELECT  id,name,age,email,delete_flag,create_time,update_time,create_user_code,update_user_code,version  FROM user  WHERE  delete_flag=0
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException:  The SQL execution time is too large, please optimize ! 
十二、代码生成器

mybatis-plus自定义模板:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bwegMJ0g-1613658092321)(…/image/image-20200802200549003.png)]

package com.example.mybatisplus.demo;

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.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 org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;

/**
 * @fileName: AutoGeneratorTest
 * @author: zengqf3
 * @date: 2020-7-17 16:32
 * @description:
 */
@RunWith(SpringRunner.class)
@SpringBootTest()
public class AutoGeneratorTest {

    @Test
    public void testGenerator() {
        // 需要构建一个 代码自动生成器 对象
        AutoGenerator mpg = new AutoGenerator();
        // 配置策略
        // 1、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("神龙飞仙");
        gc.setOpen(false);
        gc.setFileOverride(false);
        // 是否覆盖
        gc.setServiceName("%sService");
        // 去Service的I前缀
        gc.setIdType(IdType.ID_WORKER);
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);
        //2、设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis-plus? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        //3、包的配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("blog");
        pc.setParent("com.shenlongfeixian");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");
        mpg.setPackageInfo(pc);
        //4、策略配置
        StrategyConfig strategy = new StrategyConfig();
        // 设置要映射的表名
        strategy.setInclude("user");

        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        // 自动lombok;
        strategy.setLogicDeleteFieldName("deleted");
        // 自动填充配置
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);

        // 乐观锁
        strategy.setVersionFieldName("version");

        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true);
        mpg.setStrategy(strategy);
        mpg.execute();
    }
}
上一篇:装饰器


下一篇:javascript实现轮播图插件