Mybatis-Plus
一、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 您的支持。
- MySQL,Oracle,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 | 否 | "" | 指定小数点后保留的位数 |
关于
jdbcType
和typeHandler
以及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、约定
- 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
- 配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
- 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
- 默认的数据源名称为 master ,你可以通过 spring.datasource.dynamic.primary 修改。
- 方法上的注解优先于类上注解。
- DS支持继承抽象类上的DS,暂不支持继承接口上的DS
2、使用方法
- 引入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相关推荐
- mybatis查询报错:com.mysql.cj.exceptions.DataConversionException: Cannot determine value type from string
mybatis查询报错: com.mysql.cj.exceptions.DataConversionException: Cannot determine value type from strin ...
- MyBatis的插入后获得主键的方式
需求: 使用MyBatis往MySQL数据库中插入一条记录后,需要返回该条记录的自增主键值. 方法: 在mapper中指定keyProperty属性,示例如下: <insert id=" ...
- mybatis使用注解开发
mybatis使用注解开发 面向接口编程 在之前我们是通过面向对象编程,但是在真正开发的时候我们会选择面向接口编程. 根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的 ...
- mybatis ResultMap
ResultMap 解决属性名和字段的名称不一致的问题. 查询为null的问题 创建java实体类: public class User {private int id; //idprivate St ...
- mybatis配置文件解析
mybatis配置文件解析 mybatis核心配置文件`mybatis-config.xml文件. mybatis的配置文件包含了会深深影响mybatis行为的设置和属性信息. 能配置的内容: con ...
- mybatis CRUD操作
mybatis CRUD操作 select select标签是mybatis最常用的标签之一. select语句有很多属性可以详细的配置每一天sql语句. id 命名空间唯一的标识. 接口中的方法名与 ...
- java mybatis基础
java mybatis基础 1.1 什么是mybatis? mybatis是一个优秀的持久层框架. 避免几乎所有的JDBC代码和手动设置参数以及获取结果集的过程. 可以使用简单的xml或者注解来配置 ...
- mybatis的资源过滤错误及xml文件编码错误
mybatis 解决maven项目内资源过滤的问题 写的配置文件无法被导出或者生效的问题. 解决方案: <build><resources><resource>&l ...
- Mybatis传递多个参数的4种方式
现在大多项目都是使用Mybatis了,但也有些公司使用Hibernate.使用Mybatis最大的特性就是sql需要自己写,而写sql就需要传递多个参数.面对各种复杂的业务场景,传递参数也是一种学问. ...
- SpringBoot (五) :SpringBoot整合mybatis
说在前面 mybatis刚开始使用的时候比较麻烦,需要各种配置文件.实体类.dao层映射关联.还有一大推其它配置.初期开发了generator可以根据表结果自动生产实体类.配置文件和dao层代码,可以 ...
最新文章
- 收藏丨机器学习顶级数据资源 Top 8 盘点
- 使用matplotlib画图时不能同时打开太多张图
- python安装轮子_python
- mybatis-plus+springboot+vue+element-ui实现分页
- 给公司的电脑配置theano
- 当前操作系统缺少黑体等字体_2020世界互联网大会:360视觉、360守望领域、360城市运营操作系统齐亮相...
- 游戏筑基开发之简单迷宫行走(内附碰撞检测底层逻辑)
- html内容超出不自动滚动,16.css: overflow使用 例: 固定div大小,不让内容超出div
- 图论+dp poj 1112 Team Them Up!
- matlab用插值法plot,Matlab插值法
- 鸟哥Linux第二十四章-Linux内核编译与管理简记
- cad怎么画坐标系箭头_CAD中怎么画箭头啊 cad箭头
- js导出excels表格.XLSX
- 护照扫描仪出入境海关运用SDK
- java-老鼠出迷宫
- 萌新python爬虫初学
- Java编程环境搭建
- Open GL 基础
- 2022-2027年中国商用中央空调行业发展监测及投资战略研究报告
- LCD驱动分析(一)
热门文章
- 2022-2028全球石油管材接头行业调研及趋势分析报告
- iphone自适应屏幕亮度_如何降低iPhone的亮度低于iOS允许的亮度
- AndroidStudio 3.4更新了啥?
- in_array函数
- 方舟生存进化服务器信息错误,方舟生存进化网络错误信息怎么办
- 如何把照片转成pdf文件,支持合并转换
- OSChina 周一乱弹 ——为什么非洲人短跑长跑都强
- Data Lab 2(深入理解计算机系统)
- 车牌检测License Plate Detection and Recognition in Unconstrained Scenarios
- python为csv文件添加表头_csv大文件分割以及添加表头