mybatis-plus学习
前言
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
1.mybatis-plu概述
1.1、简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
我们的愿景是成为 MyBatis 最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍。
1.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 操作智能分析阻断,也可自定义拦截规则,预防误操作
1.3、pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>mybatis-plus-Demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mybatis-plus</name>
<description>mybatis-plus-Demo1</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 代码自动生成器-->
<!-- mp 代码生成器 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 导入swagger -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.20</version>
</dependency>
<!-- 添加模板引擎,thymeleaf 还有freemarker都是模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.代码生成器(重点)
package com.example;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
public class Demo3 {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
//构建一个代码自动生成器对象
AutoGenerator mpg = new AutoGenerator();
// 1、创建全局配置类的对象
GlobalConfig gc = new GlobalConfig();
//获取当前项目路径
String projectPath = System.getProperty("user.dir");
System.out.println("projectPath = " + projectPath);
//自动生成代码存放的路径
gc.setOutputDir(projectPath + "/src/main/java");
//设置 --作者注释
gc.setAuthor("LiZuFu");
//是否打开文件夹
gc.setOpen(false);
//是否覆盖已有文件
gc.setFileOverride(false);
//各层文件名称方式,例如: %sAction 生成 UserAction %s占位符
gc.setServiceName("%sService");
//设置日期策略 date类型
gc.setDateType(DateType.ONLY_DATE);
//设置主键策略 雪花算法
gc.setIdType(IdType.ASSIGN_ID);
//设置开启 swagger2 模式
gc.setSwagger2(true);
//把全局配置放入代码生成器
mpg.setGlobalConfig(gc);
// 2、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/eshop?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc); //把数据源配置加入到代码生成器
// 3、包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.example.eshop");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
// ... 有默认值,点击查看源码
mpg.setPackageInfo(pc);//包加入代码生成器
// 4、策略配置
StrategyConfig strategy = new StrategyConfig();
//下划线转驼峰命名 表
strategy.setNaming(NamingStrategy.underline_to_camel);
// 下划线转驼峰命名字段
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//实体类是否加上lombok注解
strategy.setEntityLombokModel(true);
//控制层采用RestControllerStyle注解
strategy.setRestControllerStyle(true);
// RequestMapping中 驼峰转连字符 -
strategy.setControllerMappingHyphenStyle(true);
//要映射的数据库表名 (重点)
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
//添加表名前缀
//strategy.setTablePrefix("m_"); //自动拼接上m_
//逻辑删除字段名
strategy.setLogicDeleteFieldName("deleted");
//乐观锁字段名
strategy.setVersionFieldName("version");
// -------自动填充策略
ArrayList<TableFill> fillList = new ArrayList<>();
fillList.add(new TableFill("createTime", FieldFill.INSERT));
fillList.add(new TableFill("updateTime",FieldFill.INSERT_UPDATE));
// 参数是 List<TableFill> 的链表
strategy.setTableFillList(fillList);
mpg.setStrategy(strategy);
//---------------------------------
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
//输出了 静态资源下的 Mapper
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// FreemarkerTemplateEngine模板引擎
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
3.基于mapper层CRUD
说明:
- 通用 CRUD 封装BaseMapper (opens new window)接口,为
Mybatis-Plus
启动时自动解析实体表关系映射转换为Mybatis
内部对象注入容器- 泛型
T
为任意实体对象- 参数
Serializable
为任意类型主键Mybatis-Plus
不推荐使用复合主键约定每一张表都有自己的唯一id
主键- 对象
Wrapper
为 条件构造器
1.insert
// 插入一条记录
int insert(T entity);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体对象 |
主键生成策略
因为在最开始建表的sql语句中就指明了,id是数据库的主键,主键不能唯一,
常见的数据库中主键自动设置方法有(uuid、自增id、雪花算法、redis生成、zookeeper生成)
雪花算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake。雪花算法支持的TPS可以达到419万左右(2^22*1000),几乎保证全球唯一。
雪花算法在工程实现上有单机版本和分布式版本。单机版本如下,分布式版本可以参看美团leaf算法:https://github.com/Meituan-Dianping/Leaf
设置主键id
@TableId(value = "goodID", type = IdType.ASSIGN_ID)//枚举注解,使用ID_WORKER策略,全局唯一ID,数据库设置自增也没用
private Long goodid;
查看这个注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD}) //枚举注解
public @interface TableId {
//可以设置以下两个参数
String value() default "";
IdType type() default IdType.NONE; //ID策略
}
主键自增策略
@TableId(value = "id", type = IdType.AUTO)//自增
private Integer id;
其他策略
/**
*数据库自曾ID
该类型要确保数据库设置了自曾,否则无效
*/
AUTO(0),
/**
*该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
*用户输入ID
*该类型可以通过自己注册自动填充id
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。
*分配ID (主键类型为number或string),
*/
ASSIGN_ID(3),
**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4),
@Deprecated
ID_WORKER(3),
/** @deprecated */
@Deprecated
ID_WORKER_STR(3),
/** @deprecated */
@Deprecated
UUID(4);
自动填充
-
数据库新增字段
-
在实体类的时间数据上添加@TableField
// LocalDateTime类型线程安全 /** * 默认 * DEFAULT * INSERT, * 新增时 * UPDATE, * 修改时 * INSERT_UPDATE; * 新增修改是 */ @TableField(fill = FieldFill.INSERT,value = "create_time") private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE,value = "update_time") private LocalDateTime updateTime; // FieldFill fill() default FieldFill.DEFAULT; file默认为FieldFill.DEFAULT
-
编辑处理器
package com.example.config; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Slf4j //日志 @Component//以组件的形式把这个处理器注册到IOC容器中 public class MyMetaObjectHandler implements MetaObjectHandler { //插入时启动 第三个参数 LocalDateTime 一定要和 createTime成员变量的值的类型一致,不然是null 如果是date就都设置date @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用) this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐) } //更新时候启动 @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐) } }
-
接下来的代码执行插入、更新时都会自动设置时间
Long id=Long.parseLong("01605601184784535554"); TbTypes tbTypes=goodTypes.selectById(id); tbTypes.setTypename("ipa"); tbTypes.setUpdateTime(null); System.err.println(tbTypes.toString()); int result=goodTypes.updateById(tbTypes); //通过id查询 需要把跟新时间设置为null才会更新否则还是原来修改时间 tbTypes.setUpdateTime(null);
2.delete
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
Wrapper | wrapper | 实体对象封装操作类(可以为 null) |
Collection<? extends Serializable> | idList | 主键 ID 列表(不能为 null 以及 empty) |
Serializable | id | 主键 ID |
Map<String, Object> | columnMap | 表字段 map 对象 |
逻辑删除
- 数据库添加deleted字段
-
同步实体类
@ApiModelProperty(value = "逻辑删除") @TableLogic private Integer deleted;
-
在配置文件yml中配置
spring: datasource: url: jdbc:mysql://localhost:3306/shop?characterEncoding=utf-8&servertimeZone=UTC password: 123456 username: root driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted #全局逻辑删除的实体字段名(since 3.3.0,配置可以不需要加注解2的注解) logic-delete-value: 1 #逻辑已删除值(默认为1) logic-not-delete-value: 0 #逻辑未删除值(默认为0)
-
测试
@Test
public void deleteTest(){
int result=goodTypes.deleteById(1);
System.out.println(result);
}
如果我们去查询typeID=1的用户,查不到,mybatis-plus会自动拼接了deleted在where中判断
3.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 语句) |
乐观锁
-
乐观锁机制
OptimisticLockerInnerInterceptor
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败
-
在数据库添加字段
3. 乐观锁配置需要两步
- springboot注解方式
package com.example.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement//开启事务
@Configuration//配置类注解
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//实现乐观锁
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//乐观锁插件拦截器OptimisticLockerInnerInterceptor
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//插件分页拦截器
return mybatisPlusInterceptor;
}
}
-
在实体类的字段上加上@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
不能复用!!! -
测试
@Test public void updateTest(){ Long id=Long.parseLong("1605601235107835906"); TbTypes tbTypes=goodTypes.selectById(id); tbTypes.setTypename("ipa"); tbTypes.setUpdateTime(null); System.err.println(tbTypes.toString()); TbTypes tbTypes2=goodTypes.selectById(id); tbTypes2.setTypename("ipa"); tbTypes2.setUpdateTime(null); System.err.println(tbTypes2.toString()); int result=goodTypes.updateById(tbTypes); int result2=goodTypes.updateById(tbTypes2); System.out.println(result); System.out.println(result2); }
4.select
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
参数说明
类型 | 参数名 | 描述 |
---|---|---|
Serializable | id | 主键 ID |
Wrapper | queryWrapper | 实体对象封装操作类(可以为 null) |
Collection<? extends Serializable> | idList | 主键 ID 列表(不能为 null 以及 empty) |
Map<String, Object> | columnMap | 表字段 map 对象 |
IPage | page | 分页查询条件(可以为 RowBounds.DEFAULT) |
分页查询
-
配置拦截器
package com.example.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableTransactionManagement//开启事务 @Configuration//配置类注解 public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { //实现乐观锁 MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//乐观锁插件拦截器OptimisticLockerInnerInterceptor mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//插件分页拦截器 return mybatisPlusInterceptor; } }
-
page类
Page该类继承了
IPage
类,实现了简单分页模型
如果你要实现自己的分页模型可以继承Page
类或者实现IPage
类属性名 类型 默认值 描述 records List emptyList 查询数据列表 total Long 0 查询列表总记录数 size Long 10 每页显示条数,默认 10
current Long 1 当前页 orders List emptyList 排序字段信息,允许前端传入的时候,注意 SQL 注入问题,可以使用 SqlInjectionUtils.check(...)
检查文本optimizeCountSql boolean true 自动优化 COUNT SQL 如果遇到 jSqlParser
无法解析情况,设置该参数为false
optimizeJoinOfCountSql boolean true 自动优化 COUNT SQL 是否把 join 查询部分移除 searchCount boolean true 是否进行 count 查询,如果指向查询到列表不要查询总记录数,设置该参数为 false
maxLimit Long 单页分页条数限制 countId String xml
自定义count
查询的statementId
-
使用page对象
public void PageTest(){ /**源码 * 分页构造函数 * @param current 当前页 * @param size 每页显示条数 */ Page<TbGoods> tbGoodsPage=new Page<>(1,2); userMapper.selectPage(tbGoodsPage,null); tbGoodsPage.getRecords().forEach(System.out::println); System.out.println("获取记录总行数:"+tbGoodsPage.getTotal()); }
4.wrapper(重点)
条件构造器
说明:
- 以下出现的第一个入参
boolean condition
表示该条件是否加入最后生成的sql中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)- 以下代码块内的多个方法均为从上往下补全个别
boolean
类型的入参,默认为true
- 以下出现的泛型
Param
均为Wrapper
的子类实例(均具有AbstractWrapper
的所有方法)- 以下方法在入参中出现的
R
为泛型,在普通wrapper中是String
,在LambdaWrapper
中是函数(例:Entity::getId
,Entity
为实体类,getId
为字段id
的getter Method)- 以下方法入参中的
R column
均表示数据库字段,当R
具体类型为String
时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹!)!而不是实体类数据字段名!!!,另当R
具体类型为SFunction
时项目runtime不支持eclipse自家的编译器!!!- 以下举例均为使用普通wrapper,入参为
Map
和List
的均以json
形式表现!- 使用中如果入参的
Map
或者List
为空,则不会加入最后生成的sql中!!!警告:
不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输
- wrapper 很重
- 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
- 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
- 我们拒绝接受任何关于 RPC 传输 Wrapper 报错相关的 issue 甚至 pr
AbstractWrapper
说明:
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
/**
* wrapper的使用
*/
public void WrapperTest(){
QueryWrapper<TbGoods> wrapper=new QueryWrapper<>();
//链式编程 添加查询条件
wrapper.isNotNull("goodName").gt("price","6000");
List<TbGoods> TbGoods=userMapper.selectList(wrapper);
TbGoods.forEach(System.out::println);
// eq相等 ne不相等, gt大于, lt小于 ge大于等于 le 小于等于
// equal/ not equal/ greater than/ less than/ less than or equal/ great than or equal/
}
/**
* 区间查询与计算
*/
public void WrapperTest2(){
QueryWrapper<TbGoods> wrapper=new QueryWrapper<>();
wrapper.between("price","20","26");
int count=userMapper.selectCount(wrapper);
System.out.println(count);
TbGoods tbGoods=new TbGoods();
tbGoods.setGoodname("苹果14");
tbGoods.setNum(10);
tbGoods.setPrice(BigDecimal.valueOf(6999));
tbGoods.setTypeid(3);
tbGoods.setImg("001.png");
int resul=userMapper.insert(tbGoods);
System.out.println(resul);
TbTypes tbTypes=new TbTypes();
tbTypes.setTypename("耳机");
int resut2=goodTypes.insert(tbTypes);
System.out.println(resut2);
}
/**
* 模糊查询
*/
public void WrapperTest3(){
QueryWrapper<TbGoods> wrapper=new QueryWrapper<>();
wrapper.like("goodName","苹果");
wrapper.notLike("goodName","14");
wrapper.likeRight("goodName","苹果");//苹果%
wrapper.likeLeft("goodName","9");//%9
userMapper.selectList(wrapper).forEach(System.out::println);
}
/**
* 子查询
*/
public void WrapperTest4(){
QueryWrapper<TbGoods> wrapper=new QueryWrapper<>();
wrapper.inSql("typeID","select typeID from tb_types where typeID<3");
userMapper.selectList(wrapper).forEach(System.out::println);
}
/**
* 排序
*/
public void WrapperTest5(){
//排序
QueryWrapper<TbGoods> wrapper=new QueryWrapper<>();
// wrapper.orderByAsc("goodId");//升序
wrapper.orderByDesc("goodId");//降序
userMapper.selectList(wrapper).forEach(System.out::println);
}
/**
* 分组,条件
*/
public void WrapperTest6(){
QueryWrapper<TbTypes> wrapper=new QueryWrapper<>();
wrapper.groupBy("version").having("version=2");
goodTypes.selectList(wrapper).forEach(System.out::println);
}