谷粒学院--在线教育实战项目【一】
谷粒学院--在线教育实战项目【一】
- 一、项目概述
- 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&&serverTimezone=Asia/Shanghai
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
相关文章:

谷粒学院--在线教育实战项目【一】
谷粒学院--在线教育实战项目【一】 一、项目概述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.第一…...

Power Design【数据库设计】
Power Design【数据库设计】 前言版权推荐Power Design【数据库设计】推荐11. PowerDesigner的使用11.1 开始界面11.2 概念数据模型11.3 物理数据模型11.4 概念模型转为物理模型11.5 物理模型转为概念模型11.6 物理模型导出SQL语句补充:sqlyog导入sql文件 最后 前言 2024-3-11…...

Spring Boot中Excel数据导入导出的高效实现
🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…...

采购代购系统独立站,接口采集商品上货
采购代购系统独立站的建设与商品上货接口的采集是一个综合性的项目,涉及前端开发、后端开发、数据库设计以及API接口的对接等多个环节。以下是一个大致的步骤和考虑因素: 一、系统规划与需求分析 明确业务需求:确定代购系统的核心功能&…...

Redis精讲
redis持久化 RDB方式 Redis Database Backup file (redis数据备份文件), 也被叫做redis数据快照. 简单来说就是把内存中的所有数据记录到磁盘中. 快照文件称为RDB文件, 默认是保存在当前运行目录. [rootcentos-zyw ~]# docker exec -it redis redis-cli 127.0.0.1:6379> sav…...

ELFK 分布式日志收集系统
ELFK的组成: Elasticsearch: 它是一个分布式的搜索和分析引擎,它可以用来存储和索引大量的日志数据,并提供强大的搜索和分析功能。 (java语言开发,)logstash: 是一个用于日志收集,处理和传输的…...

excel批量数据导入时用poi将数据转化成指定实体工具类
1.实现目标 excel进行批量数据导入时,将批量数据转化成指定的实体集合用于数据操作,实现思路:使用注解将属性与表格中的标题进行同名绑定来赋值。 2.代码实现 2.1 目录截图如下 2.2 代码实现 package poi.constants;/*** description: 用…...

【软件工程导论】——软工学绪论及传统软件工程(学习笔记)
📖 前言:随着软件产业的发展,计算机应用逐步渗透到社会生活的各个角落,使各行各业都发生了很大的变化。这同时也促使人们对软件的品种、数量、功能和质量等提出了越来越高的要求。然而,软件的规模越大、越复杂…...

C语言编译成库文件的要求
keil编译成库文件 在Keil中,将C语言源文件编译成库文件通常需要进行以下步骤: 创建一个新的Keil项目,并将所需的C语言源文件添加到该项目中。 在项目设置中配置编译选项,确保生成的目标文件符合库文件的标准格式。 编译项目&…...
Python的模块应用和文件I/O
Python 解释 Python是一种高级编程语言,以其简洁、易读和易用而闻名。它是一种通用的、解释型的编程语言,适用于广泛的应用领域,包括软件开发、数据分析、人工智能等。python是一种解释型,面向对象、动态数据类型的高级程序设计…...
设计模式之依赖倒转原则
目录 1、 基本介绍 2、 应用实例 3、 依赖关系传递的三种方式 (1) 接口传递 (2) 构造方法传递 (3) setter方式传递 4、 注意事项和细节 1、 基本介绍 依赖倒转原则(Dependence Inversion Principle)是指: 高层模块不应该依赖低层模块,二者都应该依…...
Springboot启动后想要做某些事可以通过什么方法实现?
在Spring Boot应用中,如果你想在应用启动完成后执行一些特定的操作(例如缓存预热),可以实现CommandLineRunner或ApplicationRunner接口。这两个接口都提供了一个run方法,在Spring Boot应用上下文初始化完成后会被自动调…...

网络原理初识(2)
目录 一、协议分层 1、分层的作用 2、OSI七层模型 3、TCP / IP五层(或四层)模型 4、网络设备所在分层 5、网络分层对应 二、封装和分用 发送过程(封装) 1、应用层(应用程序) QQ 2、传输层 3、网络层 4、数据链路层 5、物理…...
【C++】每日一题 92 反转链表
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left < right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。 class ListNode { public:int val;ListNode* next;ListNode(int _val) {val _val;next nullptr;} };…...
算法D39 | 动态规划2 | 62.不同路径 63. 不同路径 II
今天开始逐渐有 dp的感觉了,题目不多,就两个 不同路径,可以好好研究一下 62.不同路径 本题大家掌握动态规划的方法就可以。 数论方法 有点非主流,很难想到。 代码随想录 视频讲解:动态规划中如何初始化很重要&#x…...
面试官:如何在 Spring Boot 启动的时候提前运行一些特定的代码
该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:如何在 Spring Boot 启动的时候提前运行一些特定的代码 在Spring Boot启动的时候提前运行一些特定的代码可以通过实现ApplicationRunner接口、Com…...

力扣最热100题——56.合并区间
吾日三省吾身 还记得梦想吗 正在努力实现它吗 可以坚持下去吗 目录 吾日三省吾身 力扣题号:56. 合并区间 - 力扣(LeetCode) 题目描述 Java解法一:排序然后原地操作 具体代码如下 Java解法二:new一个list…...

docker学习(十四)docker搭建私服
docker私服搭建,配置域名访问,设置访问密码 启动registry docker run -d \-p 5000:5000 \-v /opt/data/registry:/var/lib/registry \registrydocker pull hello-world docker tag hello-world 127.0.0.1:5000/hello-world docker push 127.0.0.1:5000…...
基于BERTopic模型的英文20新闻数据集主题聚类及可视化
文章目录 bertopic介绍20 newsgroups dataset20 newsgroups数据集下载数据导入nltk数据处理bertopic模型构建模型训练运行模型可视化目前主题的一致性得分语料库建模bertopic介绍 BERTopic 是基于深度学习的一种主题建模方法。BERT 是一种用于 NLP 的预训练策略,它成功地利用…...

【Oracle之DataGuard的初步学习】
** 以下所有均是基于11G版本的 ** 一、DataGuard的部署方式 DG的部署最常用的方式就是直接在备库端部署一个空库然后再设置参数,但是这样做在初始同步时如果数据量过大会耗费较长的时间;相对来说这中方式比较简单不易出错。 还有一种方式就是通过rman的备…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...