一、Mybatis-Plus简介

1、简介

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

2、特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

3、支持数据库

任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。

  • MySQLOracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb
  • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库

4、框架结构

5、Maven依赖

SpringBoot

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

spring

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

注意:

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

6、配置

SpringBoot

  • 配置MapperScan注解
@SpringBootApplication
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

Spring

  • 配置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>

二、CRUD接口

1、Service CRUD 接口

说明:

通用 Service CRUD 封装IService (opens new window)接口,进一步封装 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 columnMap 表字段 map 对象
Collection idList 主键 ID 列表

Update

// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(Wrapper<T> updateWrapper);
// 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper<T> whereWrapper);
// 根据 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 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 idList 主键 ID 列表
Map columnMap 表字段 map 对象
Function 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);

2、Mapper CRUD 接口

说明:

  • 通用 CRUD 封装BaseMapper (opens new window)接口,为 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 idList 主键 ID 列表(不能为 null 以及 empty)
Serializable id 主键 ID
Map columnMap 表字段 map 对象

Update

// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 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 idList 主键 ID 列表(不能为 null 以及 empty)
Map columnMap 表字段 map 对象
IPage page 分页查询条件(可以为 RowBounds.DEFAULT)

三、常用注解

1、@TableName

  • 描述:表名注解,标识实体类对应的表
  • 使用位置:实体类
属性 类型 必须指定 默认值 描述
value String "" 表名
schema String "" schema
keepGlobalPrefix boolean false 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时)
resultMap String "" xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定)
autoResultMap boolean false 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入)
excludeProperty String[] {} 需要排除的属性名 @since 3.3.1

关于 autoResultMap 的说明:

MP 会自动构建一个 resultMap 并注入到 MyBatis 里(一般用不上),请注意以下内容:

因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 Entity 字段以及注解变化而变化),但是注入之后是静态的(等于 XML 配置中的内容)。

而对于 typeHandler 属性,MyBatis 只支持写在 2 个地方:

1.定义在 resultMap 里,作用于查询结果的封装

2.定义在 insert 和 update 语句的 #{property} 中的 property 后面(例:#{property,typehandler=xxx.xxx.xxx}),并且只作用于当前设置值

除了以上两种直接指定 typeHandler 的形式,MyBatis 有一个全局扫描自定义 typeHandler 包的配置,原理是根据您的 property 类型去找其对应的 typeHandler 并使用。

使用方法:

  • 在实体类中使用

    @TableName("sys_user")
    public class User {private Long id;private String name;private Integer age;private String email;
    }
    
  • 配置.yml文件

#设置mybatis-plus的全局配置
global-config:  db-config:   #设置实体类对应数据表的统一前缀        table-prefix: tb_

2、@TableId

  • 描述:主键注解
  • 使用位置:实体类主键字段
属性 类型 必须指定 默认值 描述
value String “” 主键字段名
type Enum IdType.NONE 指定主键类型

例如:

  • 在实体类中使用
    @TableName("sys_user")
    public class User {@TableIdprivate Long id;private String name;private Integer age;private String email;
    }
    
  • 在.yml文件中配置
#设置主键统一生成策略
id-type: auto
  • 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 )

3、@TableField

  • 描述:字段注解(非主键)
属性 类型 必须指定 默认值 描述
value String "" 数据库字段名
exist boolean true 是否为数据库表字段
condition String "" 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s}
update String "" 字段 update set 部分注入,例如:当在version字段上注解 update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性)
insertStrategy Enum FieldStrategy.DEFAULT 举例:NOT_NULL insert into table_a(column) values (#{columnProperty})
updateStrategy Enum FieldStrategy.DEFAULT 举例:IGNORED update table_a set column=#{columnProperty}
whereStrategy Enum FieldStrategy.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 UnknownTypeHandler.class 类型处理器 (该默认值不代表会按照该值生效)
numericScale String "" 指定小数点后保留的位数

关于jdbcTypetypeHandler以及numericScale的说明:

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

@TableName("sys_user")
public class User {@TableIdprivate Long id;@TableField("nickname")private String name;private Integer age;private String email;
}
  • FieldStrategy属性
描述
IGNORED 忽略判断
NOT_NULL 非 NULL 判断
NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非 NULL 判断)
DEFAULT 追随全局配置
  • FieldFill
描述
DEFAULT 默认不处理
INSERT 插入时填充字段
UPDATE 更新时填充字段
INSERT_UPDATE 插入和更新时填充字段

4、@Version

  • 描述:乐观锁注解、标记 @Version 在字段上

5、@EnumValue

  • 描述:普通枚举类注解(注解在字段上)

6、@TableLogic

  • 描述: 表字段逻辑处理注解(逻辑删除)

常见问题:

1、如何 insert?

1、字段在数据库定义默认值(推荐)2、insert 前自己 set 值3、使用自动填充功能

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

使用 update 方法并:UpdateWrapper.set(column, value)(推荐)使用 update 方法并: UpdateWrapper.setSql("column=value")*使用Sql注入器注入com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill并使用(推荐)

使用方法:

1: 配置 com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig

例 application.yml

mybatis-plus:global-config:db-config:logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

2:在实体类字段上添加@TableLogic注解

@TableLogic
private Integer deleted;

说明:

只对自动注入的 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()

附录:

·逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。

·如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。

7、@SqlParser Deprecated

@InterceptorIgnore

8、@KyeSequence

  • 描述:序列主键策略 oracle
  • 属性:value、resultMap
属性 类型 必须指定 默认值 描述
value String "" 序列名
clazz Class Long.class id 的类型, 可以指定 String.class,这样返回的 Sequence 值是字符串"1"

9、@InterceptorIgnore

见 插件章节

10、@OrdrBy

  • 描述: 内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
属性 类型 必须指定 默认值 描述
isDesc boolean true 是否倒序查询
sort short Short.MAX_VALUE 数字越小越靠前

四、条件构造器

□ Wrapper : 条件构造抽象类,最顶端父类

• AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件

​ •QueryWrapper : Entity 对象封装操作类,不是用lambda语法

​ •UpdateWrapper : Update 条件封装,用于Entity对象更新操作

​ •AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。

​ •LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper

​ •LambdaUpdateWrapper : Lambda 更新封装Wrapper

方法:

方法名 描述
eq 等于=例:eq(“name”, “老王”)—>name = '老王’
ne 不等于<>例:ne(“name”, “老王”)—>name <> '老王’
gt 大于>例: gt(“age”, 18) —>age > 18
ge 大于等于≥ 例: ge(“age”, 18) —>age ≥ 18
lt 小于< 例: lt(“age”, 18) —> age < 18
le 小于等于≤ 例: lt(“age”, 18) —> age ≤ 18
between between 值1 and 值2 例: between(“age”, 18, 30) —> age between 18 and 30
notBetween not between 值1 and 值2 例: notBetween(“age”, 18, 30) —> age not between 18 and 30
like LIKE ‘%值%’ 例: like(“name”, “王”) —> name like '%王%'
notLike NOT LIKE ‘%值%’ 例: notLike(“name”, “王”) —> name not like '%王%'
likeLeft LIKE ‘%值’ 例: likeLeft(“name”, “王”) —> name like '%王’
likeRight LIKE ‘值%’ 例: likeRight(“name”, “王”) —> name like '王%'
isNull 字段 IS NULL 例: isNull(“name”) —> name is null
isBotNull 字段 IS NOT NULL 例: isNotNull(“name”) —> name is not null
in 字段 IN (value.get(0), value.get(1), …) 例: in(“age”,{1,2,3}) —> age in (1,2,3) 字段 IN (v0, v1, …) 例: in(“age”, 1, 2, 3) —> age in (1,2,3)
notIn 字段 NOT IN (value.get(0), value.get(1), …) 例: notIn(“age”,{1,2,3}) —> age not in (1,2,3) 字段 NOT IN (v0, v1, …) 例: notIn(“age”, 1, 2, 3) —> age not in (1,2,3)
inSql 字段 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 字段 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”) —> id not in (select id from table where id < 3)
groupBy 分组:GROUP BY 字段, … 例: groupBy(“id”, “name”) —> group by id,name
orderByAsc 排序:ORDER BY 字段, … ASC 例: orderByAsc(“id”, “name”) —> order by id ASC,name ASC
orderByDesc 排序:ORDER BY 字段, … DESC 例: orderByDesc(“id”, “name”) —> order by id DESC,name DESC
orderBy 排序:ORDER BY 字段, … 例: orderBy(true, true, “id”, “name”) —> order by id ASC,name ASC
having HAVING ( sql语句 ) 例: having(“sum(age) > 10”) —> having sum(age) > 10 例: having(“sum(age) > {0}”, 11) —> having sum(age) > 11
func func 方法(主要方便在出现if…else下调用不同方法能不断链) 例: func(i -> if(true) {i.eq(“id”, 1)} else {i.ne(“id”, 1)})
or 拼接 OR 注意事项:主动调用 or 表示紧接着下一个方法不是用 and 连接!(不调用 or 则默认为使用 and 连接) 例: eq(“id”,1).or().eq(“name”,“老王”) —> id = 1 or name = '老王’
or OR 嵌套 例: or(i -> i.eq(“name”, “李白”).ne(“status”, “活着”)) —> or (name = ‘李白’ and status <> ‘活着’)
and AND 嵌套 例: and(i -> i.eq(“name”, “李白”).ne(“status”, “活着”)) —> and (name = ‘李白’ and status <> ‘活着’)
nested 正常嵌套 不带 AND 或者 OR 例: nested(i -> i.eq(“name”, “李白”).ne(“status”, “活着”)) —> (name = ‘李白’ and status <> ‘活着’)
apply 拼接 sq l 注意事项: 该方法可用于数据库函数 动态入参的 params 对应前面 applySql 内部的 {index} 部分.这样是不会有sql注入风险的,反之会有! 例: apply(“id = 1”) —> id = 1 例: 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 无视优化规则直接拼接到 sql 的最后 注意事项:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用 例: last(“limit 1”)
exists 拼接 EXISTS ( sql语句 ) 例: exists(“select id from table where age = 1”) —> exists (select id from table where age = 1)
notExists 拼接 NOT EXISTS ( sql语句 ) 例: notExists(“select id from table where age = 1”) —> not exists (select id from table where age = 1)
select 设置查询字段 ·说明:以上方法分为两类.第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要 wrapper 内的 entity 属性有值! 这两类方法重复调用以最后一次为准 例: select(“id”, “name”, “age”) 例: select(i -> i.getProperty().startsWith(“test”))
set SQL SET 字段 例: set(“name”, “老李头”) 例: set(“name”, “”) —>数据库字段值变为 空字符串 例: set(“name”, null) —>数据库字段值变为 null
setSql 设置 SET 部分 SQL 例: setSql(“name = ‘老李头’”)
lambda 获取 LambdaWrapper 在 QueryWrapper 中是获取 LambdaQueryWrapper 在 UpdateWrapper 中是获取 LambdaUpdateWrapper

1、QueryWrapper

说明:

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件

及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取

1.1、组装查询条件

@Test
public void test01() {//查询用户名含有a且年龄在20和30之间且邮箱信息不为空的用户信息//SELECT id,user_name,password,name,age,email,is_deleted FROM tb_user WHERE is_deleted=0 AND (user_name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.like("user_name", "a").between("age", 20, 30).isNotNull("email");userMapper.selectList(wrapper).forEach(System.out ::println);
}

1.2、组装排序条件

@Test
public void test02() {//查询用户信息,按照年龄的降序排序,若年龄相同,则按照id的升序排序//SELECT id,user_name,password,name,age,email,is_deleted FROM tb_user WHERE is_deleted=0 ORDER BY age DESC,id ASCQueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.orderByDesc("age").orderByAsc("id");userMapper.selectList(wrapper).forEach(System.out ::println);
}

1.3、组装删除条件

@Test
public void test03() {//删除邮箱地址为空的用户信息//UPDATE tb_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.isNull("email");int delete = userMapper.delete(wrapper);System.out.println(delete);
}

1.4、组装修改条件

@Test
public void test04() {//将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改// Preparing: UPDATE tb_user SET user_name=?, password=?, name=?, age=?, email=? WHERE is_deleted=0 AND (age > ? AND user_name LIKE ? OR email IS NULL)QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.gt("age", 20).like("user_name", "a").or().isNull("email");User user = new User();user.setName("小明");user.setEmail("test@ly.com");int i = userMapper.update(user, wrapper);System.out.println(i);
}

1.5、条件的优先级

@Test
public void test05() {//将用户名中含有a(年龄大于20或邮箱为null)的用户信息修改//lambda中的条件优先执行//UPDATE tb_user SET user_name=?, password=?, name=?, age=?, email=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.like("user_name","a").and(i->i.gt("age",20).or().isNull("email"));User user = new User();user.setName("Tom");user.setEmail("Tom@ly.com");int i = userMapper.update(user, wrapper);System.out.println(i);
}

1.6、组装select子句

@Test
public void test06() {//查询用户的用户名,年龄,邮箱信息//SELECT user_name,age,email FROM tb_user WHERE is_deleted=0QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.select("user_name","age","email");userMapper.selectMaps(wrapper).forEach(System.out::println);
}

1.7、实现子查询

@Test
public void test07() {//查询id 小于等于10的用户信息//SELECT id,user_name,password,name,age,email,is_deleted FROM tb_user WHERE is_deleted=0 AND (id IN (select id from tb_user where id <= 10))QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.inSql("id","select id from tb_user where id <= 10");userMapper.selectList(wrapper).forEach(System.out::println);
}

2、UpdateWrapper

说明:

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件

及 LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

@Test
public void test08() {//将用户名中含有a(年龄大于20或邮箱为null)的用户信息修改//UPDATE tb_user SET name=?,email=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))UpdateWrapper<User> wrapper = new UpdateWrapper<>();wrapper.like("user_name","a").and(i->i.gt("age",20).or().isNull("email"));wrapper.set("name","小黑").set("email","abc@ly.com");int i = userMapper.update(null, wrapper);System.out.println(i);
}

3、模拟开发中组装的情况

3.1、UpdateWrapper

@Test
public void test09() {//SELECT id,user_name,password,name,age,email,is_deleted FROM tb_user WHERE is_deleted=0 AND (user_name LIKE ? AND age <= ?)String username = "a";Integer ageBegin = null;Integer ageEnd = 30;QueryWrapper<User> wrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) {//isNotBlank判断某个字符串是否不为空字符串,不为null,不为空白符wrapper.like("user_name", username);}if (ageBegin != null){wrapper.ge("age",ageBegin);}if (ageEnd != null) {wrapper.le("age", ageEnd);}userMapper.selectList(wrapper).forEach(System.out::println);
}

3.2、使用condition组装条件

在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则一定不能组装,以免影响SQL执行的结果

@Test
public void test10() {//SELECT id,user_name,password,name,age,email,is_deleted FROM tb_user WHERE is_deleted=0 AND (user_name LIKE ? AND age <= ?)String username = "a";Integer ageBegin = null;Integer ageEnd = 30;QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.like(StringUtils.isNotBlank(username),"user_name","username").ge(ageBegin != null,"age","ageBegin").le(ageEnd != null,"age","ageEnd");userMapper.selectList(wrapper).forEach(System.out::println);
}

3.3、LambdaQueryWrapper

@Test
public void test11() {//Preparing: SELECT id,user_name,password,name,age,email,is_deleted FROM tb_user WHERE is_deleted=0 AND (user_name LIKE ? AND age <= ?)String username = "a";Integer ageBegin = null;Integer ageEnd = 30;LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.like(StringUtils.isNotBlank(username),User::getUserName,username).ge(ageBegin != null,User::getAge,ageBegin).le(ageEnd != null,User::getAge,ageEnd);userMapper.selectList(wrapper).forEach(System.out::println);
}

3.4、LambdaUpdateWrapper

@Test
public void test12() {//将用户名中含有a(年龄大于20或邮箱为null)的用户信息修改//UPDATE tb_user SET name=?,email=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();wrapper.like(User::getUserName, "a").and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));wrapper.set(User::getName, "小黑").set(User::getEmail, "abc@ly.com");int i = userMapper.update(null, wrapper);System.out.println(i);
}

五、插件

1、分页插件

1)配置

@Configuration
@MapperScan("com.ly.mybatisplus.mapper")
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return mybatisPlusInterceptor;}
}

2)测试

1.简单使用

@Test
public void testPage() {Page<User> page = new Page<>(1, 3);userMapper.selectPage(page,null);System.out.println(page);
}

结果:

2、获取相关参数

@Test
public void testPage() {//SELECT id,user_name,password,name,age,email,is_deleted FROM tb_user WHERE is_deleted=0 LIMIT ?Page<User> page = new Page<>(1, 3);userMapper.selectPage(page,null);System.out.println("当前页的记录:"+page.getRecords());System.out.println("总页数:"+page.getPages());System.out.println("数据总条数:"+page.getTotal());System.out.println("当前页记录的大小:"+page.getSize());System.out.println("是否有下一页:"+page.hasNext());System.out.println("是否有上一页:"+page.hasPrevious());
}

结果:

3.自定义分页功能

①在UserMapper.java中定义功能

/*** 通过用户年龄查询用户信息并分页* @param page Mybatis-Plus所提供的分页对象,并且必须在第一个参数的位置* @param age 用户年龄* @return  返回page对象*/
Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);

②在对应的.xml文件中配置对应的sql语句

配置别名

  #配置类型别名所对应的包
type-aliases-package: com.ly.mybatisplus.pojo
<!--Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);-->
<!--user 别名-->
<select id="selectPageVo" resultType="user">select id,user_name,password,name,age,email from user where age > #{age}
</select>

③编写测试方法

@Test
public void selectPageVo() {//select id,user_name,password,name,age,email from user where age > ? LIMIT ?Page<User> page = new Page<>(1,3);userMapper.selectPageVo(page, 20);System.out.println("当前页的记录:"+page.getRecords());System.out.println("总页数:"+page.getPages());System.out.println("数据总条数:"+page.getTotal());System.out.println("当前页记录的大小:"+page.getSize());System.out.println("是否有下一页:"+page.hasNext());System.out.println("是否有上一页:"+page.hasPrevious());
}

结果:

2、乐观锁 OptimisticLockerInnerInterceptor

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

乐观锁实现方式:

·取出记录时,获取当前 version

·更新时,带上这个 version

·执行更新时, set version = newVersion where version = oldVersion

·如果 version 不对,就更新失败

乐观锁配置需要两步:

1.配置插件

Spring Boot注解方式

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;
}

2.在实体类的字段上加上@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.1.0开始,如果你无需使用原生枚举,可配置默认枚举来省略扫描通用枚举配置 默认枚举配置

**升级说明:**3.1.0 以下版本改变了原生默认行为,升级时请将默认枚举设置为EnumOrdinalTypeHandler

**影响用户:**实体中使用原生枚举

**其他说明:**配置枚举包扫描的时候能提前注册使用注解枚举的缓存

1、声明通用枚举属性

方式一: 使用 @EnumValue 注解枚举属性

public enum GradeEnum {PRIMARY(1, "小学"),  SECONDORY(2, "中学"),  HIGH(3, "高中");GradeEnum(int code, String descp) {this.code = code;this.descp = descp;}@EnumValue//标记数据库存的值是codeprivate final int code;//。。。
}

方式二: 枚举属性,实现 IEnum 接口如下:

public enum AgeEnum implements IEnum<Integer> {ONE(1, "一岁"),TWO(2, "二岁"),THREE(3, "三岁");private int value;private String desc;@Overridepublic 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;
}

2、配置扫描通用枚举

配置文件 resources/application.yml

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

自定义配置类 MybatisPlusAutoConfiguration


@Configuration
public class MybatisPlusAutoConfiguration {@Beanpublic MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {return properties -> {GlobalConfig globalConfig = properties.getGlobalConfig();globalConfig.setBanner(false);MybatisConfiguration configuration = new MybatisConfiguration();configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class);properties.setConfiguration(configuration);};}
}

3**、如何序列化枚举值为数据库存储值?**

3.1、Jackson

3.1.1、重写 toString 方法

①springboot
    @Beanpublic Jackson2ObjectMapperBuilderCustomizer customizer(){return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);}
②Jackson
    ObjectMapper objectMapper = new ObjectMapper();objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);

以上两种方式任选其一,然后在枚举中复写 toString 方法即可.

3.1.2、注解处理

public enum GradeEnum {PRIMARY(1, "小学"),  SECONDORY(2, "中学"),  HIGH(3, "高中");GradeEnum(int code, String descp) {this.code = code;this.descp = descp;}@EnumValueprivate final int code;
}

在.yml文件中配置扫描通用枚举

#扫描通用枚举的包
type-enums-package: com.ly.mybatisplus.enums

3.2、Fastjson

3.2.1、重写 toString 方法

①全局处理方式
    FastJsonConfig config = new FastJsonConfig();config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString);
②局部处理方式
    @JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)private UserStatus status;

以上两种方式任选其一,然后在枚举中复写 toString 方法即可.

七、代码生成器

1、导入依赖

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.2</version>
</dependency>
<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.31</version>
</dependency>

2、使用

2.1、快速生成

FastAutoGenerator.create("url", "username", "password").globalConfig(builder -> {builder.author("baomidou") // 设置作者.enableSwagger() // 开启 swagger 模式.fileOverride() // 覆盖已生成文件.outputDir("D://"); // 指定输出目录}).packageConfig(builder -> {builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名.moduleName("system") // 设置父包模块名.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://")); // 设置mapperXml生成路径}).strategyConfig(builder -> {builder.addInclude("t_simple") // 设置需要生成的表名.addTablePrefix("t_", "c_"); // 设置过滤表前缀}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板.execute();

2.2、交互生成

FastAutoGenerator.create(DATA_SOURCE_CONFIG)// 全局配置.globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称?")).fileOverride())// 包配置.packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名?")))// 策略配置.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all"))).controllerBuilder().enableRestStyle().enableHyphenStyle().entityBuilder().enableLombok().addTableFills(new Column("create_time", FieldFill.INSERT)).build())/*模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker.templateEngine(new BeetlTemplateEngine()).templateEngine(new FreemarkerTemplateEngine())*/.execute();// 处理 all 情况
protected static List<String> getTables(String tables) {return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}

八、多数据源

一个基于springboot的快速集成多数据源的启动器

这是一个第三方 mybatis 扩展库,与 mybatis-plus 本身无关,属于组织参与者小锅盖个人发起的项目,任何行为与 baomidou 组织其它成员无关。

1、多数据源简介

1.1、简介

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

其支持 Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.x.x。

1.2、特性

  • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  • 支持数据库敏感配置信息 加密 ENC()。
  • 支持每个数据库独立初始化表结构schema和数据库database。
  • 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
  • 支持 自定义注解 ,需继承DS(3.2.0+)。
  • 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
  • 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
  • 提供 自定义数据源来源 方案(如全从数据库加载)。
  • 提供项目启动后 动态增加移除数据源 方案。
  • 提供Mybatis环境下的 纯读写分离 方案。
  • 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
  • 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
  • *提供 *基于seata的分布式事务方案。
  • 提供 本地多数据源事务方案。

1.3、约定

  1. 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
  2. 配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
  3. 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
  4. 默认的数据源名称为 master ,你可以通过 spring.datasource.dynamic.primary 修改。
  5. 方法上的注解优先于类上注解。
  6. DS支持继承抽象类上的DS,暂不支持继承接口上的DS

2、使用方法

  1. 引入dynamic-datasource-spring-boot-starter。
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version3.5.1</version>
</dependency>

2.配置数据源。

spring:datasource:dynamic:primary: master #设置默认的数据源或者数据源组,默认值即为masterstrict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源datasource:master:url: jdbc:mysql://xx.xx.xx.xx:3306/dynamicusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置slave_1:url: jdbc:mysql://xx.xx.xx.xx:3307/dynamicusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverslave_2:url: ENC(xxxxx) # 内置加密,使用请查看详细文档username: ENC(xxxxx)password: ENC(xxxxx)driver-class-name: com.mysql.jdbc.Driver#......省略#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
# 多主多从                      纯粹多库(记得设置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 切换数据源。

@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。

注解 结果
没有@DS 默认数据源
@DS(“dsName”) dsName可以为组名也可以为具体某个库的名称
@Service
@DS("slave")
public class UserServiceImpl implements UserService {@Autowiredprivate JdbcTemplate jdbcTemplate;public List selectAll() {return  jdbcTemplate.queryForList("select * from user");}@Override@DS("slave_1")public List selectByCondition() {return  jdbcTemplate.queryForList("select * from user where age >10");}
}

4、测试

①配置数据源

spring:datasource:dynamic:primary: master #设置默认的数据源或者数据源组,默认值即为masterstrict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源datasource:master:url: jdbc:mysql://localhost:3306/mp?characterEncoding=utf-8&userSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置slave_1:url: jdbc:mysql://localhost:3306/mp_1?characterEncoding=utf-8&userSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver

②创建UserService

public interface UserService extends IService<User> {}@Service
@DS("master")  //指定数据源
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}

③创建ProductService

public interface ProductService  extends IService<Product> {}@Service
@DS("slave_1") //指定数据源
public class ProductServiceImpl extends ServiceImpl<ProductMapper , Product> implements ProductService {}

④测试

@Autowired
private UserService userService;@Autowired
private ProductService productService;@Test
public void test() {System.out.println(userService.getById(1));System.out.println(productService.getById(1));
}

测试结果:

e.queryForList(“select * from user where age >10”);
}
}


## 4、测试### **①配置数据源**```yml
spring:datasource:dynamic:primary: master #设置默认的数据源或者数据源组,默认值即为masterstrict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源datasource:master:url: jdbc:mysql://localhost:3306/mp?characterEncoding=utf-8&userSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置slave_1:url: jdbc:mysql://localhost:3306/mp_1?characterEncoding=utf-8&userSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver

②创建UserService

public interface UserService extends IService<User> {}@Service
@DS("master")  //指定数据源
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}

③创建ProductService

public interface ProductService  extends IService<Product> {}@Service
@DS("slave_1") //指定数据源
public class ProductServiceImpl extends ServiceImpl<ProductMapper , Product> implements ProductService {}

④测试

@Autowired
private UserService userService;@Autowired
private ProductService productService;@Test
public void test() {System.out.println(userService.getById(1));System.out.println(productService.getById(1));
}

测试结果:

Mybatis-Plus相关推荐

  1. mybatis查询报错:com.mysql.cj.exceptions.DataConversionException: Cannot determine value type from string

    mybatis查询报错: com.mysql.cj.exceptions.DataConversionException: Cannot determine value type from strin ...

  2. MyBatis的插入后获得主键的方式

    需求: 使用MyBatis往MySQL数据库中插入一条记录后,需要返回该条记录的自增主键值. 方法: 在mapper中指定keyProperty属性,示例如下: <insert id=" ...

  3. mybatis使用注解开发

    mybatis使用注解开发 面向接口编程 在之前我们是通过面向对象编程,但是在真正开发的时候我们会选择面向接口编程. 根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的 ...

  4. mybatis ResultMap

    ResultMap 解决属性名和字段的名称不一致的问题. 查询为null的问题 创建java实体类: public class User {private int id; //idprivate St ...

  5. mybatis配置文件解析

    mybatis配置文件解析 mybatis核心配置文件`mybatis-config.xml文件. mybatis的配置文件包含了会深深影响mybatis行为的设置和属性信息. 能配置的内容: con ...

  6. mybatis CRUD操作

    mybatis CRUD操作 select select标签是mybatis最常用的标签之一. select语句有很多属性可以详细的配置每一天sql语句. id 命名空间唯一的标识. 接口中的方法名与 ...

  7. java mybatis基础

    java mybatis基础 1.1 什么是mybatis? mybatis是一个优秀的持久层框架. 避免几乎所有的JDBC代码和手动设置参数以及获取结果集的过程. 可以使用简单的xml或者注解来配置 ...

  8. mybatis的资源过滤错误及xml文件编码错误

    mybatis 解决maven项目内资源过滤的问题 写的配置文件无法被导出或者生效的问题. 解决方案: <build><resources><resource>&l ...

  9. Mybatis传递多个参数的4种方式

    现在大多项目都是使用Mybatis了,但也有些公司使用Hibernate.使用Mybatis最大的特性就是sql需要自己写,而写sql就需要传递多个参数.面对各种复杂的业务场景,传递参数也是一种学问. ...

  10. SpringBoot (五) :SpringBoot整合mybatis

    说在前面 mybatis刚开始使用的时候比较麻烦,需要各种配置文件.实体类.dao层映射关联.还有一大推其它配置.初期开发了generator可以根据表结果自动生产实体类.配置文件和dao层代码,可以 ...

最新文章

  1. 收藏丨机器学习顶级数据资源 Top 8 盘点
  2. 使用matplotlib画图时不能同时打开太多张图
  3. python安装轮子_python
  4. mybatis-plus+springboot+vue+element-ui实现分页
  5. 给公司的电脑配置theano
  6. 当前操作系统缺少黑体等字体_2020世界互联网大会:360视觉、360守望领域、360城市运营操作系统齐亮相...
  7. 游戏筑基开发之简单迷宫行走(内附碰撞检测底层逻辑)
  8. html内容超出不自动滚动,16.css: overflow使用 例: 固定div大小,不让内容超出div
  9. 图论+dp poj 1112 Team Them Up!
  10. matlab用插值法plot,Matlab插值法
  11. 鸟哥Linux第二十四章-Linux内核编译与管理简记
  12. cad怎么画坐标系箭头_CAD中怎么画箭头啊 cad箭头
  13. js导出excels表格.XLSX
  14. 护照扫描仪出入境海关运用SDK
  15. java-老鼠出迷宫
  16. 萌新python爬虫初学
  17. Java编程环境搭建
  18. Open GL 基础
  19. 2022-2027年中国商用中央空调行业发展监测及投资战略研究报告
  20. LCD驱动分析(一)

热门文章

  1. 2022-2028全球石油管材接头行业调研及趋势分析报告
  2. iphone自适应屏幕亮度_如何降低iPhone的亮度低于iOS允许的亮度
  3. AndroidStudio 3.4更新了啥?
  4. in_array函数
  5. 方舟生存进化服务器信息错误,方舟生存进化网络错误信息怎么办
  6. 如何把照片转成pdf文件,支持合并转换
  7. OSChina 周一乱弹 ——为什么非洲人短跑长跑都强
  8. Data Lab 2(深入理解计算机系统)
  9. 车牌检测License Plate Detection and Recognition in Unconstrained Scenarios
  10. python为csv文件添加表头_csv大文件分割以及添加表头