Mybatis-Plus

2023-05-16

一、Mybatis-Plus简介

1、简介

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

Mybatis-plus

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

参数说明

类型参数名描述
Tentity实体对象
CollectionentityList实体对象集合
intbatchSize插入批次数量

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

参数说明

类型参数名描述
Tentity实体对象
WrapperupdateWrapper实体对象封装操作类 UpdateWrapper
CollectionentityList实体对象集合
intbatchSize插入批次数量

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)

参数说明

类型参数名描述
WrapperqueryWrapper实体包装类 QueryWrapper
Serializableid主键 ID
MapcolumnMap表字段 map 对象
CollectionidList主键 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);

参数说明

类型参数名描述
WrapperupdateWrapper实体对象封装操作类 UpdateWrapper
Tentity实体对象
CollectionentityList实体对象集合
intbatchSize更新批次数量

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

参数说明

类型参数名描述
Serializableid主键 ID
WrapperqueryWrapper实体对象封装操作类 QueryWrapper
booleanthrowEx有多个 result 是否抛出异常
Tentity实体对象
Functionmapper转换函数

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

参数说明

类型参数名描述
WrapperqueryWrapper实体对象封装操作类 QueryWrapper
CollectionidList主键 ID 列表
MapcolumnMap表字段 map 对象
Functionmapper转换函数

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

参数说明

类型参数名描述
IPagepage翻页对象
WrapperqueryWrapper实体对象封装操作类 QueryWrapper

Count

// 查询总记录数
int count();
// 根据 Wrapper 条件,查询总记录数
int count(Wrapper<T> queryWrapper);

参数说明

类型参数名描述
WrapperqueryWrapper实体对象封装操作类 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);

参数说明

类型参数名描述
Tentity实体对象

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)

参数说明

类型参数名描述
Wrapperwrapper实体对象封装操作类(可以为 null)
CollectionidList主键 ID 列表(不能为 null 以及 empty)
Serializableid主键 ID
MapcolumnMap表字段 map 对象

Update

// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);

参数说明

类型参数名描述
Tentity实体对象 (set 条件值,可为 null)
WrapperupdateWrapper实体对象封装操作类(可以为 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);

参数说明

类型参数名描述
Serializableid主键 ID
WrapperqueryWrapper实体对象封装操作类(可以为 null)
CollectionidList主键 ID 列表(不能为 null 以及 empty)
MapcolumnMap表字段 map 对象
IPagepage分页查询条件(可以为 RowBounds.DEFAULT)

三、常用注解

1、@TableName

  • 描述:表名注解,标识实体类对应的表
  • 使用位置:实体类
属性类型必须指定默认值描述
valueString""表名
schemaString""schema
keepGlobalPrefixbooleanfalse是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时)
resultMapString""xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定)
autoResultMapbooleanfalse是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入)
excludePropertyString[]{}需要排除的属性名 @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

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

例如:

  • 在实体类中使用
    @TableName("sys_user")
    public class User {
        @TableId
        private Long id;
        private String name;
        private Integer age;
        private String email;    
    }
    
  • 在.yml文件中配置
#设置主键统一生成策略
id-type: auto
  • IdType属性
描述
AUTO数据库 ID 自增
NONE无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUTinsert 前自行 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 )
UUID32 位 UUID 字符串(please use ASSIGN_UUID )
ID_WORKER_STR分布式全局唯一 ID 字符串类型(please use ASSIGN_ID )

3、@TableField

  • 描述:字段注解(非主键)
属性类型必须指定默认值描述
valueString""数据库字段名
existbooleantrue是否为数据库表字段
conditionString""字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s}
updateString""字段 update set 部分注入,例如:当在version字段上注解 update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性)
insertStrategyEnumFieldStrategy.DEFAULT举例:NOT_NULL insert into table_a(column) values (#{columnProperty})
updateStrategyEnumFieldStrategy.DEFAULT举例:IGNORED update table_a set column=#{columnProperty}
whereStrategyEnumFieldStrategy.DEFAULT举例:NOT_EMPTY where column=#{columnProperty}
fillEnumFieldFill.DEFAULT字段自动填充策略
selectbooleantrue是否进行 select 查询
keepGlobalFormatbooleanfalse是否保持使用全局的 format 进行处理
jdbcTypeJdbcTypeJdbcType.UNDEFINEDJDBC 类型 (该默认值不代表会按照该值生效)
typeHandlerClassUnknownTypeHandler.class类型处理器 (该默认值不代表会按照该值生效)
numericScaleString""指定小数点后保留的位数

关于jdbcTypetypeHandler以及numericScale的说明:

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

@TableName("sys_user")
public class User {
    @TableId
    private 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
属性类型必须指定默认值描述
valueString""序列名
clazzClassLong.classid 的类型, 可以指定 String.class,这样返回的 Sequence 值是字符串"1"

9、@InterceptorIgnore

见 插件章节

10、@OrdrBy

  • 描述: 内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
属性类型必须指定默认值描述
isDescbooleantrue是否倒序查询
sortshortShort.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
betweenbetween 值1 and 值2 例: between(“age”, 18, 30) —> age between 18 and 30
notBetweennot between 值1 and 值2 例: notBetween(“age”, 18, 30) —> age not between 18 and 30
likeLIKE ‘%值%’ 例: like(“name”, “王”) —> name like '%王%'
notLikeNOT LIKE ‘%值%’ 例: notLike(“name”, “王”) —> name not like '%王%'
likeLeftLIKE ‘%值’ 例: likeLeft(“name”, “王”) —> name like '%王’
likeRightLIKE ‘值%’ 例: 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
havingHAVING ( sql语句 ) 例: having(“sum(age) > 10”) —> having sum(age) > 10 例: having(“sum(age) > {0}”, 11) —> having sum(age) > 11
funcfunc 方法(主要方便在出现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 = '老王’
orOR 嵌套 例: or(i -> i.eq(“name”, “李白”).ne(“status”, “活着”)) —> or (name = ‘李白’ and status <> ‘活着’)
andAND 嵌套 例: 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”))
setSQL 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 ASC
    QueryWrapper<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=0
    QueryWrapper<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 {
  @Bean
  public 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//标记数据库存的值是code
    private final int code;
    //。。。
}

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

public enum AgeEnum implements IEnum<Integer> {
    ONE(1, "一岁"),
    TWO(2, "二岁"),
    THREE(3, "三岁");

    private int value;
    private String desc;

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

实体属性使用枚举类型

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

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


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

2、配置扫描通用枚举

配置文件 resources/application.yml

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

自定义配置类 MybatisPlusAutoConfiguration


@Configuration
public class MybatisPlusAutoConfiguration {

    @Bean
    public 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
    @Bean
    public 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;
    }

    @EnumValue
    private 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 #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave_1:
          url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
        slave_2:
          url: ENC(xxxxx) # 内置加密,使用请查看详细文档
          username: ENC(xxxxx)
          password: ENC(xxxxx)
          driver-class-name: com.mysql.jdbc.Driver
       #......省略
       #以上会配置一个默认库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 {

  @Autowired
  private 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 #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mp?characterEncoding=utf-8&userSSL=false
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave_1:
          url: jdbc:mysql://localhost:3306/mp_1?characterEncoding=utf-8&userSSL=false
          username: root
          password: 123456
          driver-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 #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mp?characterEncoding=utf-8&userSSL=false
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave_1:
          url: jdbc:mysql://localhost:3306/mp_1?characterEncoding=utf-8&userSSL=false
          username: root
          password: 123456
          driver-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));
}

测试结果:

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mybatis-Plus 的相关文章

  • Docker镜像、容器操作

    文章目录 一 Docker镜像操作1 搜索镜像2 获取镜像3 查看镜像查看下载到本地的所有镜像查看下载的镜像文件信息查看镜像详细信息 4 为本地的镜像添加新的标签5 镜像导出导入到本地导出镜像 xff0c 将镜像保存为本地文件导入镜像 xf
  • Windows系统中gvim永久配置行号和背景颜色

    1 gvim永久配置 1 永久配置行号 点击编辑 启动设定 如下图所示 在下图画框位置输入set number保存即可 2 xff09 永久设置背景颜色 首先应该知道都有什么颜色 xff0c 可以设置的颜色按照下图查看 比如现在我要设置背景

随机推荐

  • UKF无迹卡尔曼滤波

    UKF无迹卡尔曼滤波是在卡尔曼滤波和变换的基础上发展而来的 xff0c 它是利用无损变换使线性假设下的卡尔曼滤波应用于非线性系统 之前提到的EKF算法简单易操作 xff0c 在工业中有广泛的应用 但是它也存在很多缺点 xff1a 需要计算非
  • 在失望中重找希望——我的2013年工作总结

    时间过的真的是快 来广州已整整工作了一年啦 从2012年长沙工作离职后 为了我的女朋友 我毅然踏上了南下广州的征途 来到羊城后 很快 xff0c 一个礼拜就找到了现在工作的这家公司 现在回想一下 真觉得当初没有好好斟酌一下 2013年里 x
  • 使用eclipse 4.3 经常出现卡死、无响应情况的解决方法

    最近在使用 eclipse 4 3 开发的时候 xff0c 经常出现卡死 无响应 情况 在网上搜索了一下之后发现 xff0c 发现网上还是有解决方法的 于是以记之 xff01 一 首先 xff0c 我们修改下eclipse的内存配置文件 l
  • Android学习之 移动应用<App>微信支付集成小结

    微信支付现在主要集成在 xff1a 1 移动应用开发 2 网站应用开发 3 公众账号开发 本篇主要针对移动应用App集成微信支付 xff0c 实际项目坑点分享 xff01 一 既予之 与共之 xff1a 平台资源 1 微信开放平台 xff1
  • Android学习之 主项目合并Library子项目中的Manifest

    一 项目背景 xff1a 项目XX是一个按模块化规则来进行开发的 xff0c 包含主模块A 子模块B 子模块C 子模块D xff0c 其中子模块B C D都是Library项目 xff0c 并且都包含有自己的Actity等资源文件 Andr
  • 我的2011——周年纪

    今天距我开博的日期 xff0c 有将近一年半的时间 xff0c 博客的确是一个会让我们有所期待的东西 xff0c 学习 积累 沉淀 再学习 2011年 xff0c 经历了血雨腥风的SAP市场开始主推云存储和内存运算 xff0c HANA在不
  • Android学习之 Manifest中meta-data扩展元素数据的配置与获取

    在AndroidManifest xml清单文件中 我们有时会看到如下类似的 lt meta data gt 元素开始的配置内容 xff1a lt meta data android name 61 34 com google androi
  • 工具使用之 adbWireless无线调试Android应用

    今天巧遇这个工具 xff1a adbwireless apk xff0c 于是乎 试爽了一把 xff0c 果然觉得是个不错的工具 可谓是相见恨晚 可以帮助Android开发的同事们实现手机无线调试应用程序 对 xff01 你没有听错 如果你
  • Android系统 小米/三星/索尼 应用启动图标未读消息数(BadgeNumber)动态提醒

    在Android手机上 xff0c 如QQ 微信当有未读消息的时候 我们可以看到在应用的启动图标的右上角会有一个红色圈圈 且圈圈里会动态显示未读消息的数目 xff0c 如下图显示 xff1a 那么该功能是怎么实现的呢 xff1f 在万能的互
  • 工具使用之Android Studio快捷键-mac版

    最近给自己添置了一台mac 也算是完成了多年前的一个小愿望 做为Android开发者的我于是搭载了Android Studio 1 1正式版做为了我的安卓开发工具 在window上eclipse我可以畅快的玩耍 xff0c idea和as也
  • Android学习之 Scroller的介绍与使用

    类概述 Android里Scroller类是为了实现View平滑滚动的一个Helper类 通常在自定义的View时使用 xff0c 在View中定义一个私有成员mScroller 61 new Scroller context 设置mScr
  • Android 从外部网页拉起跳转到App

    业务场景 当需要从外部第三方网页中通过点击某个链接或按钮启动App应用程序 实现 新建demo工程 xff0c 并实现一个Activity xff0c 用来接收从外部跳转传入的信息 代码如下 xff1a span class hljs ke
  • Android 使用ColorMatrix改变图片颜色

    ColorMatrix的颜色矩阵介绍 颜色矩阵M是一个5 4的矩阵 xff0c 在Android中 xff0c 颜色矩阵M是以一维数组m 61 a b c d e f g h i j k l m n o p q r s t 的方式进行存储的
  • 中断处理handler不能sleep

    1 进入中断处理程序 gt 2 保存关键上下文 gt 3 开中断 xff08 sti指令 xff09 gt 4 进入中断处理程序的handler gt 5 关中 里面很多说法不是很同意 个人认为中断处理handler不能sleep原因应该不
  • opencv移动物体追踪

    本次试验用的WINFORM 要先绘制窗体 xff0c 自己测试的时候注意对象名就可以了 public Form1 InitializeComponent static Mat mat1 61 new Mat 64 34 timg jpg 3
  • 2021-10-5 每天几个LCEDA小知识——放置探测点

    立创EDA专业版之放置探测点 放置探测点 放置探测点 在PCBA验证环节中 探测点是很重要的存在 试想一下 一个产品在生产测试的时候 没有预留探测点 用于产品的调试 那么作业人员只能用探头去勾元器件 何其难受 因此在设计PCB的过程中 预留
  • KairosDB 1.13安装手记

    PS xff1a 为了处理监控数据 xff0c 我们需要一个时间序列数据库 xff0c OpenTSDB是前驱 xff0c 但是是基于Hbase实现的 xff0c 后来有了一个基于Cassandra的实现 xff0c 就是KairosDB
  • FreeRTOS(V8.0.1)系统之vTaskDelete()

    lt pre name 61 34 code 34 class 61 34 objc 34 gt void vTaskDelete TaskHandle t xTaskToDelete TCB t pxTCB taskENTER CRITI
  • PX4 pixhawk 和APM2.X 的USB驱动都是不能够在 windows 7 、windows 8的ghost系统下自动安装(已解决)

    PX4 pixhawk 和APM2 X 的USB驱动都是不能够在 windows 7 windows 8的ghost系统下自动安装的 xff0c 因为这ghost系统精简了一些不该精简的东西 解决方法有两个 xff1a 一 重新装完整版的操
  • Mybatis-Plus

    一 Mybatis Plus简介 1 简介 MyBatis Plus opens new window xff08 简称 MP xff09 是一个 MyBatis opens new window 的增强工具 xff0c 在 MyBatis