谷粒学院--在线教育实战项目【一】
- 一、项目概述
- 1.1.项目来源
- 1.2.功能简介
- 1.3.技术架构
- 二、Mybatis-Plus概述
- 2.1.简介
- 2.2.特性
- 三、Mybatis-Plus入门
- 3.1.创建数据库
- 3.2.创建 User 表
- 3.3.初始化一个SpringBoot工程
- 3.4.在Pom文件中引入SpringBoot和Mybatis-Plus相关依赖
- 3.5.第一次使用lombok需要在IDEA中安装lombok插件
- 3.6.MySQL 数据库的相关配置
- 3.7.编写代码
- 3.7.1.主类:
- 3.7.2.实体类:
- 3.7.3.mapper:
- 3.8.开始使用
- 3.8.1.查询所有用户,代码测试
- 3.8.2.UserMapper(加注解)
- 3.8.3.运行结果
- 3.9.配置日志
- 四、Mybatis-Plus相关操作
- 4.1.插入操作【insert】
- 4.2.主键生成策略
- 4.2.1.ID_WORKER
- 4.2.2.自增策略
- 4.3.根据Id更新操作【update】
- 4.4.自动填充
- 4.4.1.数据库表中添加自动填充字段
- 4.4.2.实体上添加注解
- 4.4.3.测试添加数据
- 4.4.4.测试结果
- 4.4.5.自动填充策略FieldFill
- 4.4.6.自定义实现类 MyMetaObjectHandler实现元对象处理器接口
- 4.4.7.测试添加数据
- 4.4.8.测试结果
- 4.5.乐观锁
- 4.5.1.表中添加version字段,作为乐观锁版本号
- 4.5.2.实体类添加version属性
- 4.5.3.元对象处理器接口添加version的insert默认值
- 4.5.4.创建配置类MybatisPlusConfig 并注册 Bean
- 4.5.5.测试乐观锁可以修改成功
- 4.5.6.测试乐观锁修改失败
- 4.6.select【查询】
- 4.6.1.根据id查询记录
- 4.6.2.通过多个id批量查询
- 4.6.3.简单的条件查询
- 4.6.4.分页
- 4.6.4.1.创建配置类-添加分页插件
- 4.6.4.2.测试selectPage分页
- 4.6.4.3.测试selectMapsPage分页:结果集是Map
- 4.7.delete【物理删除】
- 4.7.1.根据id删除记录
- 4.7.2.批量删除
- 4.7.3.简单的条件查询删除
- 4.8.【逻辑删除】
- 4.8.1.数据库中添加 deleted字段
- 4.8.2.实体类添加deleted 字段并加上 @TableLogic 逻辑删除注解
- 4.8.3.元对象处理器接口添加deleted的insert默认值
- 4.8.4.application.properties 加入配置
- 4.8.5.测试逻辑删除
- 4.8.6.测试逻辑删除后的查询
- 五、性能分析
- 5.1.配置插件
- 5.1.1.参数说明
- 5.1.2.在 MybatisPlusConfig 中配置
- 5.1.3.自定义实现类 MyMetaObjectHandler
- 5.1.4.Spring Boot 中设置dev环境
- 5.2.测试
- 5.2.1.常规测试
- 5.2.2.将maxTime 改小之后再次进行测试
- 六、条件构造器 Wrapper
- endl 小技巧:护眼模式:CAE6CA、C7EDCC
一、项目概述
1.1.项目来源
谷粒学院在线教育平台采用B2C商业模式
,使用前后端分离开发方式
。项目包含后台管理系统
和前台用户系统
,两个系统中分别包含后端接口部分和前端页面部分。
系统后端
接口部分,使用目前流行的SpringBoot+SpringCloud进行微服务架构,使用Feign、Gateway、Hystrix,以及阿里巴巴的Nacos等组件搭建了项目的基础环境。项目中还使用MyBatisPlus进行持久层的操作,使用了OAuth2+JWT实现了分布式的访问,项目中整合了SpringSecurity进行了权限控制。除此之外,项目中使用了阿里巴巴的EasyExcel实现对Excel的读写操作,使用了Redis进行首页数据的缓存,使用Git进行代码的版本控制,还整合了Swagger生成接口文档 。
系统前端
部分,使用主流的前端框架Vue,使用ES6的开发规范,采用模块化的开发模式,搭建页面环境使用了Nuxt框架和vue-admin-template模板,使用Element-ui进行页面布局。前端环境中使用Npm进行依赖管理,使用Babel进行代码转换,使用Webpack进行静态资源的打包,采用axios进行Ajax请求调用,使用了ECharts进行数据的图表展示。
1.2.功能简介
谷粒学院,是一个B2C模式的职业技能在线教育系统,分为前台用户系统和后台运营平台。
1.3.技术架构
二、Mybatis-Plus概述
2.1.简介
Mybatis-Plus官网:https://baomidou.com/
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
2.2.特性
无侵入
:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑损耗小
:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作强大的 CRUD 操作
:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求支持 Lambda 形式调用
:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错支持多种数据库
:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库支持主键自动生成
:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题支持 XML 热加载
:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动支持 ActiveRecord 模式
:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作支持自定义全局通用操作
:支持全局通用方法注入( Write once, use anywhere )支持关键词自动转义
:支持数据库关键词(order、key…)自动转义,还可自定义关键词内置代码生成器
:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用内置分页插件
:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询内置性能分析插件
:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询内置全局拦截插件
:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作内置 Sql 注入剥离器
:支持 Sql 注入剥离,有效预防 Sql 注入攻击
三、Mybatis-Plus入门
3.1.创建数据库
-- utf8mb4字符集和utf8mb4_unicode_ci排序规则被用于新创建的数据库
CREATE DATABASE mybatis_plus CHARACTER SET utf8 COLLATE utf8_unicode_ci;
3.2.创建 User 表
表结构
id | name | age | |
---|---|---|---|
1 | Jone | 18 | test1@baomidou.com |
2 | Jack | 20 | test2@baomidou.com |
3 | Tom | 28 | test3@baomidou.com |
4 | Sandy | 21 | test4@baomidou.com |
5 | Billie | 24 | test5@baomidou.com |
-- 建表SQL语句
use mybatis_plus;DROP TABLE IF EXISTS user;CREATE TABLE user(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id)
);-- 插入数据SQL语句
INSERT INTO user (id, name, age, email) VALUES(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),(5, 'Billie', 24, 'test5@baomidou.com');select * from user;
3.3.初始化一个SpringBoot工程
版本:2.2.1.RELEASE
3.4.在Pom文件中引入SpringBoot和Mybatis-Plus相关依赖
spring-boot-starter、spring-boot-starter-test
添加:mybatis-plus-boot-starter、MySQL、lombok
注意:引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 MyBatis-Spring,以避免因版本差异导致的问题。
<?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.2.1.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.orange</groupId><artifactId>mybatis_plus_demo</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--lombok用来简化实体类--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
3.5.第一次使用lombok需要在IDEA中安装lombok插件
3.6.MySQL 数据库的相关配置
在 application.properties
配置文件中添加 MySQL 数据库的相关配置:
#mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.147.133:3306/mybatis_plus?characterEncoding=utf-8&&useSSL=false
spring.datasource.username=root
spring.datasource.password=Mysql.123456
-- 查看MySQL的版本mysql -VSELECT VERSION();
3.7.编写代码
3.7.1.主类:
在 Spring Boot 启动类中添加 @MapperScan
注解,扫描 Mapper 文件夹
注意
:扫描的包名根据实际情况修改
@SpringBootApplication
@MapperScan("com.orange.mybatisplus.mapper")
public class MybatisPlusDemoApplication {public static void main(String[] args) {SpringApplication.run(MybatisPlusDemoApplication.class, args);}}
3.7.2.实体类:
创建包 entity 编写实体类 User.java
/*** Description: User实体类*/
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}
查看编译结果
3.7.3.mapper:
创建包 mapper 编写Mapper 接口: UserMapper.java
@Repository
public interface UserMapper extends BaseMapper<User> {}
3.8.开始使用
3.8.1.查询所有用户,代码测试
@SpringBootTest
public class MybatisPlusDemoApplicationTests {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelectList() {System.out.println(("----- selectAll method test ------"));//UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper//所以不填写就是无任何条件List<User> users = userMapper.selectList(null);users.forEach(System.out::println);}}
3.8.2.UserMapper(加注解)
@Repository
public interface UserMapper extends BaseMapper<User> {
}
注意:
IDEA在 UserMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。
为了避免报错,可以在 dao 层 的接口上添加 @Repository
注解
3.8.3.运行结果
----- selectAll method test ------
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
3.9.配置日志
#查看sql输出日志
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
----- selectAll method test ------
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3e6534e7] was not registered for
synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@118363130 wrapping com.mysql.cj.jdbc.ConnectionImpl@6ff415ad]
will not be managed by Spring
==> Preparing: SELECT id,name,age,email FROM user
==> Parameters:
<== Columns: id, name, age, email
<== Row: 1, Jone, 18, test1@baomidou.com
<== Row: 2, Jack, 20, test2@baomidou.com
<== Row: 3, Tom, 28, test3@baomidou.com
<== Row: 4, Sandy, 21, test4@baomidou.com
<== Row: 5, Billie, 24, test5@baomidou.com
<== Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3e6534e7]
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
四、Mybatis-Plus相关操作
4.1.插入操作【insert】
//添加数据操作@Testpublic void addUser(){User user = new User();user.setName("libai");user.setAge(18);user.setEmail("123456@163.com");int result = userMapper.insert(user);System.out.println("insert:"+result); //影响的行数System.out.println(user); //id自动回填}
注意:数据库插入id值默认为:全局唯一id
4.2.主键生成策略
4.2.1.ID_WORKER
MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID
参考资料:分布式系统唯一ID生成方案汇总
:https://www.cnblogs.com/haoxinyue/p/5208136.html
4.2.2.自增策略
- 要想主键自增需要配置如下主键策略
- 需要在创建
数据表
的时候设置主键自增
- 实体字段中配置 @TableId(type = IdType.AUTO)
- 需要在创建
@TableId(type = IdType.AUTO)
private Long id;
要想影响所有实体的配置,可以设置全局主键配置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
其它主键策略:分析 IdType 源码可知
public enum IdType {AUTO(0),//数据库ID自增NONE(1),//该类型为未设置主键类型INPUT(2),//用户输入ID 该类型可以通过自己注册自动填充插件进行填充/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */ID_WORKER(3),//全局唯一ID (idWorker)UUID(4),//全局唯一ID (UUID)ID_WORKER_STR(5);//字符串全局唯一ID (idWorker 的字符串表示)private int key;private IdType(int key) {this.key = key;}public int getKey() {return this.key;}}
4.3.根据Id更新操作【update】
注意:update时生成的sql自动是动态sql:UPDATE user SET age=? WHERE id=?
//修改操作@Testpublic void updateUserById() {User user = new User();user.setId(1L);user.setName("tiny");user.setAge(30);int row = userMapper.updateById(user);System.out.println(row);}
4.4.自动填充
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间
,更新时间
等。
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:
4.4.1.数据库表中添加自动填充字段
在User表中添加datetime类型的新的字段 create_time
、update_time
-- 删除字段
ALTER TABLE user drop createTime,drop updateTime;
-- 删除单个字段 alter table 表名称 drop 字段;
-- 删除多个字段 alter table 表名称 drop 字段1,drop 字段2,drop 字段3;-- 添加字段
ALTER TABLE userADD COLUMN create_time datetime COMMENT '创建时间',ADD COLUMN update_time datetime COMMENT '更新时间';desc user;show create table user;
4.4.2.实体上添加注解
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {//@TableId(type = IdType.ID_WORKER) //mp自带策略,生成19位值,数字类型使用这种策略,比如long//@TableId(type = IdType.ID_WORKER_STR) //mp自带策略,生成19位值,字符串类型使用这种策略//主键自动增长策略//@TableId(type = IdType.AUTO)private Long id;private String name;private Integer age;private String email;//自动填充属性添加注解//注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置//create_time@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;//update_time//@TableField(fill = FieldFill.UPDATE)@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;
}
4.4.3.测试添加数据
//添加用户@Testpublic void addUserAuto() {User user = new User();user.setName("dufu");user.setAge(60);user.setEmail("123456@163.com");//手动设置时间值user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());int result = userMapper.insert(user);System.out.println("insert:" + result); //影响的行数System.out.println(user); //id自动回填}
4.4.4.测试结果
4.4.5.自动填充策略FieldFill
public enum FieldFill {/*** 默认不处理*/DEFAULT,/*** 插入填充字段*/INSERT,/*** 更新填充字段*/UPDATE,/*** 插入和更新填充字段*/INSERT_UPDATE;private FieldFill() {}
}
4.4.6.自定义实现类 MyMetaObjectHandler实现元对象处理器接口
注意:不要忘记添加 @Component 注解
/*** Description: 自定义实现类 MyMetaObjectHandler 自动填充功能*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {//使用mq实现添加操作,这个方法执行@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill ....");//根据名称设置属性值//this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);// 也可以使用(3.3.0 该方法有bug)this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());// 起始版本 3.3.0(推荐使用)//this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class);// 起始版本 3.3.3(推荐)this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}//使用mq实现修改操作,这个方法执行@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill ....");this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}
}
4.4.7.测试添加数据
//添加用户@Testpublic void addUserAuto() {User user = new User();user.setName("baijuyi");user.setAge(110);user.setEmail("111111@163.com");int result = userMapper.insert(user);System.out.println("insert:" + result); //影响的行数System.out.println(user); //id自动回填}
4.4.8.测试结果
4.5.乐观锁
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
4.5.1.表中添加version字段,作为乐观锁版本号
-- 添加字段
ALTER TABLE user ADD COLUMN version INT COMMENT '版本号';desc user;
4.5.2.实体类添加version属性
并添加 @Version 注解
@Version@TableField(fill = FieldFill.INSERT)private Integer version;//版本号
4.5.3.元对象处理器接口添加version的insert默认值
@Overridepublic void insertFill(MetaObject metaObject) {......this.strictInsertFill(metaObject, "version", Integer.class, 1);}
特别说明:
- 支持的数据类型只有 int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下
newVersion = oldVersion + 1
newVersion
会回写到entity
中- 仅支持
updateById(id)
与update(entity, wrapper)
方法 - 在
update(entity, wrapper)
方法下,wrapper
不能复用!!!
4.5.4.创建配置类MybatisPlusConfig 并注册 Bean
/*** Description: Mybatis-plus配置类*/
@Configuration
@MapperScan("com.orange.mybatisplus.mapper")
public class MybatisPlusConfig {/*** 乐观锁插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}
4.5.5.测试乐观锁可以修改成功
测试后分析打印的sql语句,将version的数值进行了加1操作
//测试 乐观锁插件 添加数据@Testpublic void testOptimisticLockerAddUser() {User user = new User();user.setName("wangwu");user.setAge(100);user.setEmail("100000@163.com");int result = userMapper.insert(user);System.out.println("insert:" + result); //影响的行数System.out.println(user); //id自动回填}//测试 乐观锁插件@Testpublic void testOptimisticLocker() {//查询User user = userMapper.selectById(1765668893893959681L);System.out.println(user);//修改数据user.setName("lisi");user.setEmail("lisi@qq.com");//执行更新userMapper.updateById(user);System.out.println(user);}
4.5.6.测试乐观锁修改失败
//测试乐观锁插件 失败@Testpublic void testOptimisticLockerFail() {//查询User user = userMapper.selectById(1765668893893959681L);System.out.println(user);//修改数据user.setName("lisi1");user.setEmail("lisi@qq.com1");//模拟取出数据后,数据库中version实际数据比取出的值大,即已被其它线程修改并更新了versionuser.setVersion(user.getVersion() - 1);//执行更新userMapper.updateById(user);System.out.println(user);}
4.6.select【查询】
4.6.1.根据id查询记录
//根据id查询记录@Testpublic void testSelectById() {User user = userMapper.selectById(1L);System.out.println("user = " + user);}
4.6.2.通过多个id批量查询
//通过多个id批量查询//完成了动态sql的foreach的功能@Testpublic void testSelectBatchIds() {List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));userList.forEach(System.out::println);}
4.6.3.简单的条件查询
//简单的条件查询//通过map封装查询条件@Testpublic void testSelectByMap() {HashMap<String, Object> hashMap = new HashMap<>();//注意:map中的key对应的是数据库中的列名。//例如数据库user_id,实体类是userId,这时map的key需要填写user_idhashMap.put("name", "lisi");hashMap.put("age", 100);List<User> userList = userMapper.selectByMap(hashMap);userList.forEach(System.out::println);}
4.6.4.分页
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
4.6.4.1.创建配置类-添加分页插件
@Configuration
@MapperScan("com.orange.mybatisplus.mapper")
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//添加分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType//添加乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}
4.6.4.2.测试selectPage分页
测试:最终通过page对象获取相关数据
//分页查询@Testpublic void testSelectPage() {//创建Page对象//传入两个参数:当前页 和 每页显示记录数//Page<User> page = new Page<>(1, 3);Page<User> page = new Page<>(2, 3);//调用mp分页查询的方法//调用mq分页查询过程中,底层封装//把分页所有数据封装到page对象里面userMapper.selectPage(page, null);//通过page对象获取分页数据page.getRecords().forEach(System.out::println);System.out.println("当前页: " + page.getCurrent());System.out.println("每页数据list集合: " + page.getRecords());System.out.println("每页显示记录数: " + page.getSize());System.out.println("总页数: " + page.getPages());System.out.println("总记录数: " + page.getTotal());System.out.println("是否有下一页: " + page.hasNext());System.out.println("是否有上一页: " + page.hasPrevious());}
4.6.4.3.测试selectMapsPage分页:结果集是Map
//测试selectMapsPage分页:结果集是Map@Testpublic void testSelectMapsPage() {//创建Page对象//传入两个参数:当前页 和 每页显示记录数Page page = new Page<>(2, 3);//调用mp查询的方法IPage<Map<String, Object>> mapsPage = userMapper.selectMapsPage(page, null);//注意:此行必须使用 mapIPage 获取记录列表,否则会有数据类型转换错误mapsPage.getRecords().forEach(System.out::println);System.out.println("当前页: " + page.getCurrent());System.out.println("每页数据list集合: " + page.getRecords());System.out.println("每页显示记录数: " + page.getSize());System.out.println("总页数: " + page.getPages());System.out.println("总记录数: " + page.getTotal());System.out.println("是否有下一页: " + page.hasNext());System.out.println("是否有上一页: " + page.hasPrevious());}
4.7.delete【物理删除】
4.7.1.根据id删除记录
//根据id删除记录@Testpublic void testDeleteById() {int result = userMapper.deleteById(1L);System.out.println(result);}
4.7.2.批量删除
//批量删除@Testpublic void testDeleteBatchIds() {int result = userMapper.deleteBatchIds(Arrays.asList(2, 3, 4));System.out.println(result);}
4.7.3.简单的条件查询删除
//简单的条件查询删除@Testpublic void testDeleteByMap() {HashMap<String, Object> map = new HashMap<>();map.put("name", "baijuyi");map.put("age", 110);int result = userMapper.deleteByMap(map);System.out.println(result);}
4.8.【逻辑删除】
物理删除:
真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据逻辑删除:
假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
4.8.1.数据库中添加 deleted字段
-- 添加字段
ALTER TABLE user ADD COLUMN deleted boolean default 0 COMMENT '被删除状态';desc user;
4.8.2.实体类添加deleted 字段并加上 @TableLogic 逻辑删除注解
并加上 @TableLogic 注解 和 @TableField(fill = FieldFill.INSERT) 注解
//实体类字段上加上@TableLogic注解@TableLogic@TableField(fill = FieldFill.INSERT)private Integer deleted;
4.8.3.元对象处理器接口添加deleted的insert默认值
@Overridepublic void insertFill(MetaObject metaObject) {......this.strictInsertFill(metaObject, "deleted", Integer.class, 0);}
4.8.4.application.properties 加入配置
此为默认值,如果你的默认值和mp默认的一样,该配置可无
# mybatis-plus.global-config.db-config.logic-delete-field=deleted # 全局逻辑删除的实体字段名
mybatis-plus.global-config.db-config.logic-delete-value=1 # 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-not-delete-value=0 # 逻辑未删除值(默认为 0)
4.8.5.测试逻辑删除
- 测试后发现,数据并没有被删除,deleted字段的值由0变成了1
- 测试后分析打印的sql语句,是一条update
- 注意:被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
//测试 逻辑删除@Testpublic void testLogicDelete() {int result = userMapper.deleteById(5L);System.out.println(result);}
4.8.6.测试逻辑删除后的查询
MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断
//测试 逻辑删除后的查询:不包括被逻辑删除的记录@Testpublic void testLogicDeleteSelect() {User user = new User();List<User> users = userMapper.selectList(null);users.forEach(System.out::println);}
测试后分析打印的sql语句,包含 WHERE deleted=0
SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE deleted=0
五、性能分析
性能分析拦截器,用于输出每条 SQL 语句及其执行时间
SQL 性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题
5.1.配置插件
5.1.1.参数说明
- 参数:maxTime: SQL 执行最大时长,超过自动停止运行,有助于发现问题。
- 参数:format: SQL是否格式化,默认false。
5.1.2.在 MybatisPlusConfig 中配置
mybatis-plus中3.2.0 版本之后把PerformanceInterceptor功能被废除
<!--mybatis-plus 3.4.2改为3.0.5测试性能 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.0.5</version></dependency>
/*** Description: Mybatis-plus配置类*/
@Configuration
@MapperScan("com.orange.mybatisplus.mapper")
public class MybatisPlusConfig {/*@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//添加分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType//添加乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}*///乐观锁插件@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() {return new OptimisticLockerInterceptor();}/*** 分页插件*/@Beanpublic PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();}//逻辑删除插件@Beanpublic ISqlInjector sqlInjector() {return new LogicSqlInjector();}/*** SQL 执行性能分析插件* 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长** 三种环境* * dev:开发环境* * test:测试环境* * prod:生产环境*/@Bean@Profile({"dev","test"})// 设置 dev test 环境开启public PerformanceInterceptor performanceInterceptor() {PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();performanceInterceptor.setMaxTime(500);//ms,超过此处设置的ms则sql不执行performanceInterceptor.setFormat(true);return performanceInterceptor;}
}
5.1.3.自定义实现类 MyMetaObjectHandler
/*** Description: 自定义实现类 MyMetaObjectHandler 自动填充功能*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {//使用mq实现添加操作,这个方法执行@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill ....");//根据名称设置属性值//this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);// 也可以使用(3.3.0 该方法有bug)/*this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());// 起始版本 3.3.0(推荐使用)//this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class);// 起始版本 3.3.3(推荐)this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "version", Integer.class, 1);this.strictInsertFill(metaObject, "deleted", Integer.class, 0);*/this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);this.setFieldValByName("version", 1, metaObject);this.setFieldValByName("deleted", 0, metaObject);}//使用mq实现修改操作,这个方法执行@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill ....");//this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);}
}
5.1.4.Spring Boot 中设置dev环境
#环境设置:dev、test、prod
spring.profiles.active=dev
可以针对各环境新建不同的配置文件application-dev.properties
、application-test.properties
、application-prod.properties
也可以自定义环境名称:如test1、test2
5.2.测试
5.2.1.常规测试
/*** Description:测试性能*/
@SpringBootTest
public class Perform {@Autowiredprivate UserMapper userMapper;//测试 性能分析插件@Testpublic void testPerformance() {User user = new User();user.setName("我是Helen");user.setEmail("helen@sina.com");user.setAge(18);userMapper.insert(user);}
}
输出:
5.2.2.将maxTime 改小之后再次进行测试
performanceInterceptor.setMaxTime(1);//ms,超过此处设置的ms不执行
如果执行时间过长,则抛出异常:The SQL execution time is too large,
输出:
六、条件构造器 Wrapper
复杂条件查询,需要使用条件构造器 Wrapper
- Wrapper : 条件构造抽象类,最顶端父类
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper : Entity 对象封装操作类,不是用lambda语法
- UpdateWrapper : Update 条件封装,用于Entity对象更新操作
- AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
- LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
endl 小技巧:护眼模式:CAE6CA、C7EDCC