文章目录
1、尚硅谷-谷粒学院
1.1、系统功能模块介绍
后台系统:
1、讲师管理
2、课程分类管理
3、课程管理
1.视频管理
4、视频信息统计分析
5、订单管理
6、轮播图banner管理
7、权限管理
前台系统:
1、首页数据的显示
2、讲师列表和详情
3、课程列表和课程的详情
1.课程详情
2.课程观看
3.课程评论
4、登录和注册
1.微信扫码登录
5、微信扫码支付
1.2、系统开发方式
前后端分离开发
全栈开发
后端技术:SpringBoot、SpringCloud、SpringSecurity、Redis、Maven、EasyExcel、Jwt、Oauth2
前端技术:Vue、Element-UI、axios、node.js
其它技术:阿里云OSS,阿里云视频点播、阿里云短信服务、微信支付和登录、docker、jenkins、git
2、Mybatis-Plus
2.1、什么是 MyBatis?
https://mybatis.net.cn/getting-started.html
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2.2、什么是Mybatis-Plus?
https://baomidou.com/pages/24112f/
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
2.3、Mybatis-plus 的特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
2.4、支持的数据库
MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb
达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库
3、Mybatis-Plus入门
3.1、创建表和数据
表和数据的脚本来源于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)
);
DELETE FROM user;
#插入数据
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');
3.2、创建SpringBoot工程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-87gCXMkO-1690788257982)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805092526643.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-akmT3Fgl-1690788257987)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805092625620.png)]
视频中使用的版本是2.2.1,最好选择稳定版本,不要选择最新的版本,这里我选择2.7.2版本的SpringBoot,是稳定版
3.3、安装Mybatis-Plus
在pom文件中,写入以下的依赖。
如果出现依赖无法加载的情况,请更新maven的镜像库(阿里云的比较好)。
<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>
</dependency>
<!--Mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--数据库连接包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
</dependencies>
3.3.1 问题:pom依赖显示红色,无法更新依赖
提示信息:
No valid Maven installation found. Either set the home directory in the configuration dialog or set the M2_HOME environment variable on your system.
没有找到 可用的maven 安装设置。要么在配置对话框中设置主(home)目录或者在你的系统上设置M2_HOME环境变量。
Either...or... 要么...要么...
configuration 配置
dialog 对话
原因:
IDEA的Maven地址设置出错,找不到指定的maven路径
解决方案:在IDEA中配置好Maven的位置,包括setting.xml和repository的位置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oTHY01b3-1690788257992)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805094250837.png)]
https://blog.csdn.net/Julycaka/article/details/83185621
3.3.2 安装Lombok插件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RP8acKdP-1690788257997)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805095431661.png)]
插件里面搜索Lombok,安装
3.4 、配置application.properties
application.yaml或者application.properties都可以
src/ main/ resources
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisstudy?serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=li1473606768
3.5、实体类的编写
使用Lombok,减少代码量。
@Data会给该类的所有私有属性自动添加Get和Set方法。
@AllArgsConstructor是有参,@NoArgsConstructor是无参
实体类User
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dtCjW2zE-1690788258002)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805100429878.png)]
3.6、创建Mapper接口
使接口类继承BaseMapper
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AuZulL2T-1690788258006)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805100834752.png)]
继承之后,按住Control点击BaseMapper这个类,就可以看到里面自动生成的方法。
然后我们还需要对SpringBoot的启动类添加一个MapperScan的注解,注解的参数为实际放置Mapper的包的路径:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IThr9Op4-1690788258011)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805101028092.png)]
@Repository是sping注解需要配合@MapperScan使用,@Mapper是mybatis注解可以单独使用
3.7、测试
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XnTmiHZN-1690788258016)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805101641707.png)]
如果注入的userMapper出现红色波浪线提示,则可以在userMapper类中声明@Repository或者@Service将这个类声明为一个Bean对象
/**
* @discription 查看数据库中user表的数据
* @author Macieserenity
* @updateTime 2022/8/5 11:33
* @return
* @throws
*/
@Test
void contextLoads() {
//查询user中的所有数据
List<User> users=userMapper.selectList(null);
System.out.println("============查询");
System.out.println("users==>"+users);
}
运行test类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-99MzZPU7-1690788258020)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805101921958.png)]
[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.8、Mybatis-plus的日志打印
在application.properties文件中加入:
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CGfgsrdU-1690788258025)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805112630879.png)]
使用select * 的话会降低效率
https://blog.csdn.net/jiangchao858/article/details/115676371
4、Mybatis实现CRUD
4.1、实现添加功能
/**
* @discription 往数据库中添加一条记录
* @author Macieserenity
* @updateTime 2022/8/5 11:32
* @return
* @throws
*/
@Test
void addUser(){
User user =new User();
user.setAge(18);
user.setEmail("hello@123.com");
user.setName("makabaka");
int result=userMapper.insert(user);
System.out.println("============插入");
System.out.println("insert==>"+result);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JmxCaPgb-1690788258030)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805113608121.png)]
可以看到我们没有设置User 的id属性,但是查看日志可以看到,Mybatis-Plus自动为我们生成了19位Long主键的值。
4.1.1、主键的生成策略
注意:3.5版的mybatis-plus 已经将ID_WORKER改名叫ASSIGN_ID了
自动增长
Auto_increment
缺点:很难进行分表分库存储,要进行分表存储时,需要得到上一张表的长度,在此长度上+1,作为当前表的起始ID
UUID
每次生成一个随机且唯一的值
缺点:无法进行排序,
Redis实现
redis生成ID,由于redis是单线程的,可以生成全局唯一的ID,可以用redis的原子操作INCR和INCRBY来实现。
Mybatis-Plus中自带的策略
雪花算法
生成一个long型的ID,使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit是机器的ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID)。
最后一个是符号位,永远是0。
3.9.2、配置主键生成策略
在实体类的id属性上增加一个
@TableId(type=IdType.AUTO)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYN4kO69-1690788258034)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805114937801.png)]
注意:3.5版的mybatis-plus 已经将ID_WORKER和ID_WORKER_STR改名叫ASSIGN_ID了
然后按住control键,点击AUTO,进入查看里面的策略
AUTO:主键字段自增
实体类的主键字段的注解 @TableId(type = IdType.AUTO);
数据表对应的字段一定也要设置成自增;
如果只加了注解,但没有将数据表对应的主键字段设置为自增,则将会出现如下的异常!
NONE:未设置主键
由于每一张数据表都是有主键的,因此几乎没有添加该注解的必要。
INPUT:需要开发者手动赋值
INPUT 如果开发者没有手动赋值,则数据库通过设置自增方式给主键赋值(MP不介入主键生成),同样地,如果数据库也没有设置自增则会抛出异常;如果开发者手动赋值,则直接存入该值。
ASSIGN_ID:MyBatis-Plus 自动赋值
ASSIGN_ID 如果用户或开发者没有为主键赋值,则由 MyBatis-Plus 采用 snowflake 算法(也被称为雪花算法)为主键字段分配一个ID,这个 ID 可以是 Number(Long) 类型,也可以是 String 类型。
ASSIGN_UUID:MyBatis-Plus 分配 UUID,必须是 String 类型
ASSIGN_UUID 的用法与 ASSIGN_ID 类似,可以表示更长的唯一的 ID,但其主键的数据类型必须是 String,由 MyBatis-Plus 自动生成 UUID 进行赋值;
雪花算法:snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为 毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味 着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯 一!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zPhKOrAh-1690788258039)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805115030254.png)]
参考文章:
https://blog.csdn.net/m0_61961937/article/details/126048049
https://blog.csdn.net/qq_41775769/article/details/123045089
4.2、实现更改数据
/**
* @author Macieserenity
* <br/>
* Update 更新数据
*/
@Test
void updateUser(){
User user =new User();
user.setId(2L);
user.setAge(18);
user.setEmail("hello@123Update.com");
user.setName("makabaka");
int result=userMapper.updateById(user);
System.out.println("update result==>"+result);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-REtTAH1D-1690788258044)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805132726484.png)]
4.2.1、自动填充
往表中添加两个字段
#在表user中添加create_time date类型 和 update_time date类型两个字段.
mysql> alter table user add create_time date;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table user add update_time date;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
在实体类中添加对应的属性
注意命名的特点
在java中,使用驼峰式命名:createTime
在数据库中,不区分大小写,所以用下划线分割两个单词:create_time
手动填充方式:
@Test
void addUserDate(){
User user =new User();
user.setAge(22);
user.setEmail("hello@123.comMakabaka");
user.setName("makabakaUpdateDate");
//手动添加时间
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
int result=userMapper.insert(user);
System.out.println("============插入");
System.out.println("insert==>"+result);
}
Mybatis-plus:自动填充
不需要手动set值,使用mybatis-plus框架的方式
//第一步:在实体类里面使用注解声明为自动填充
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xD8lgqet-1690788258048)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805134140700.png)]
或者可以把updateTime的自动填充类型设置为
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-elJU9x49-1690788258053)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805134229981.png)]
这样,在插入和更新的时候,就会自动进行填充。
//第二步:创建类实现(implement)接口MetaObjectHandler
//实现insertFill和updateFill两个方法
//在类上添加@Component声明为Spring组件,交由Spring管理
//目录:
package com.study.mybatisplusstudy.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import java.util.Date;
/**
* className: MyMetaObjectHandler<br/>
* author: MacieSerenity<br/>
* date: 2022-08-05 13:44<br/>
**/
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* discription 在insert语句执行时,对设定的属性进行自动填充 <br/>
* author Macieserenity <br/>
* param: metaObject <br/>
* updateTime 2022/8/5 13:55 <br/>
* return <br/>
* throws <br/>
*/
@Override
public void insertFill(MetaObject metaObject) {
//第一个参数:数据库中需要自动填充的字段,取Java实体类的属性名称作为参数
//第二个参数:需要填充的数据
//第三个参数:源数据
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
/**
* discription 在进行数据的更新时,进行数据的自动填充 <br/>
* author Macieserenity <br/>
* param: metaObject <br/>
* updateTime 2022/8/5 13:58 <br/>
* return <br/>
* throws <br/>
*/
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
然后使用test类,进行测试
//
@Test
void addUserDate(){
User user =new User();
user.setAge(22);
user.setEmail("hello@123.comMakabaka");
user.setName("makabakaUpdateDate");
int result=userMapper.insert(user);
System.out.println("============插入");
System.out.println("insert==>"+result);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8O6NO8eh-1690788258058)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805141705861.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dDjCKTi3-1690788258063)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805141641874.png)]
4.3、实现简单查询
/**
* @discription 查看数据库中user表的数据
* @author Macieserenity
* @updateTime 2022/8/5 11:33
* @return
* @throws
*/
@Test
void contextLoads() {
//查询user中的所有数据
List<User> users=userMapper.selectList(null);
System.out.println("============查询");
System.out.println("users==>"+users);
}
@Test
void selectUserById(){
//通过ID查询
User userById = userMapper.selectById(1555455304972189698L);
//通过ID批量查询
List<User> users1 = userMapper.selectBatchIds(Arrays.asList(1555455304972189698L, 1555437406157303809L, 1555436878589341698L));
System.out.println("userById===>"+userById);
System.out.println("users1===>"+users1);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DoXYCgp-1690788258068)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805155131130.png)]
根据Map实现条件查询:
@Test
void selectUserByMap(){
HashMap<String,Object> map=new HashMap<>();
map.put("name","Helen");
map.put("age",18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gk2mS6c7-1690788258073)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805155545943.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qOCD7kII-1690788258079)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805155557911.png)]
4.4、实现逻辑删除
4.4.1、物理实现
/**
* discription 删除ID为1555396057806725121的用户 <br/>
* author Macieserenity <br/> <br/>
* updateTime 2022/8/5 14:44 <br/>
* return <br/>
* throws <br/>
*/
@Test
void deleteUser(){
User user =new User();
user.setId(1555396057806725121L);
int result = userMapper.deleteById(user);
System.out.println("delete result==>"+result);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3VrFHTDw-1690788258084)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805144411160.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cjNu5pYX-1690788258089)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805144435406.png)]
4.4.2、批量删除
同时删除多条记录
@Test
void deleteMultiple(){
int result = userMapper.deleteBatchIds(Arrays.asList(1,2));
}
4.4.3、条件删除
@Test
void deleteByMap(){
HashMap<String,Object> map=new HashMap<>();
map.put("name","Helen");
map.put("age",18);
int result = userMapper.deleteByMap(map);
}
根据map进行条件删除
4.4.3、逻辑删除
原理:在表中做一个标志位,更改标志位,select时将该标志位标记为已删除的记录不可见或者跳过,完成逻辑删除。
该插件默认0没有删除,1删除
可以到配置文件中进行更改
mybatis-plus.global-config.db-confg.logic-delete-value=1
mybatis-plus.global-config.db-confg.logic-no-delete-value=0
第一步:在数据库中添加deleted字段
alter table user drop column deleted;
alter table user add deleted int default 0;
第二步:在实体类对应的属性上添加注解
@TableLogic
@TableField(fill=FieldFill.INSERT)
需要增加的语句:
//---User.java
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
//---MyMetaObjectHandler.java
this.setFieldValByName("deleted",1,metaObject);
加上逻辑删除的插件:
mybatis-plus 3.5版本及以上,就不需要配置插件了
只需要在实体类中使用对应的组件和创建数据库时添加对应的字段即可
详情:
https://blog.csdn.net/qq_57581439/article/details/124050671
第三步:测试
@Test
void logicDelete(){
int i = userMapper.deleteById(1555437406157303809L);
System.out.println(i==1?"删除了":"删除了但是没完全删除");
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-REcjlvqz-1690788258094)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805165252612.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YxdfpN2h-1690788258099)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805165403779.png)]
此时我们进行查询:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1r8iIFSu-1690788258105)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805165515044.png)]
查看日志文件可以知道,自动为我们过滤了被逻辑删除的查询
//注意
//一旦加上了逻辑删除插件,mybatis-plus将无法查询到被逻辑删除的数据(即便加了deleted=1)
//只能使用xml文件,自定义一个SQL语句进行查询。
5、乐观锁与悲观锁
5.1、事务的隔离性
是为了解决 丢失更新 问题
事务的隔离性:脏读 不可重复读
不可重复读和幻读是初学者不易分清的概念,总的来说,解决不可重复读的方法是 锁行,解决幻读的方式是 锁表。
事物的隔离性:
#读未提交:read uncomitted(最低的隔离级别)
什么是读未提交?
事务A可以读取到事务B未提交的数据。
这种隔离级别存在的问题就是:
Dirty Read 脏读现象
读到了脏数据
例如:有事务A和事务B都有以下DML语句:
更新,查询,更新,查询...
假设事务A和B并行执行,且我们
这种隔离级别都试了理论上的,大多数的数据库隔离级别都是在 第二个读已提交 级别之后的。
#读已提交:read committed
什么是读已提交?
事务A只能读到事务B提交之后的数据。
这种隔离级别解决了脏读现象。
不可重复读数据。
事务在开启之后,第一次和第二次读取的数据可能不会相同,所以不可重复读。
这种隔离级别是比较真实的数据,每次读取的都是绝对的真实数据。
例如:事务A和事务B 两个事务中都有多条DML语句如下:
插入,查询,插入,查询...
当事务A和事务B并行执行时,我们假设事务A在事务B执行到一半时提交了
那么事务B的第二次读取就能读取到事务A已经提交的数据,而第一次读取到的也还是事务A未更改之前的数据。
只能读取到已经提交的数据
oracle数据库默认的隔离级别就是 read committed
#可重复读:repeatable read
什么是可重复读?
事务开启之后,无论多久,在事务A中读取到的数据都是一致的,即使事务B已经将数据修改且提交了,但是事务A读取到的数据还是不会发生改变,这就是可重复读。
解决了不可重复读数据的问题。
但是会产生幻影读(每一次读取的数据都是幻象,不够真实)。
#序列化/串行化:serializable (最高的隔离级别)
什么是序列化?
是最高级别的隔离,效率最低,解决了所有的问题。
这种隔离级别表示事务排队,不能并发。
synchronize,线程同步(事务同步)
每一次读取到的数据都是最真实的,且效率最低。
5.2、乐观锁
取出记录时记录版本号,执行事务完毕后确定当前数据版本号是不是读取时候的版本,修改后版本相同时,更改版本号,若版本号不同,则回滚。
异步,支持并发
场景:
抢票系统,只能有一个人能抢到
5.3、悲观锁
串行,同步阻塞
5.4、配置乐观锁
在表中添加一个version字段,作为乐观锁的版本字段
alter table user add version int;
然后在实体类中添加version注解:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-koHiv39X-1690788258110)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805152722808.png)]
这里TableField跟上面一样,插入时赋值为1
配置类
配置类是springBoot提供的一个专门用于配置的类,使用@Configuration注解声明,为的是避免启动类中有过多的配置方法,可以讲这些配置方法专门写入一个配置类中,方便管理。
配置一个乐观锁插件
我们新建一个包叫做config里面存我们的配置类,我们可以把刚才写在主入口函数上的MapperScan一起写到该配置类中,然后将从官网找到乐观锁插件(记得要比对自己的Mybatis-Plus的版本号,它不同版本的插件好像有略微不同)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmH6AcGH-1690788258115)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805153308864.png)]
//可以从官方网站copy下来
https://baomidou.com/pages/0d93c0/#optimisticlockerinnerinterceptor
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
此时我们编写一个测试类
注意:必须先查再改
@Test
void testVersion(){
User user = userMapper.selectById(1555455304972189698L);
user.setAge(2000);
userMapper.updateById(user);
}
然后我们可以通过select数据库中的数据查看执行前和执行后的数据变化:
可以看到version变化了,age变成了2000
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HnEcm3sK-1690788258120)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805154515922.png)]
6、分页查询
与pageHelper类似
6.1、配置分页插件
https://baomidou.com/pages/97710a/#%E5%B1%9E%E6%80%A7%E4%BB%8B%E7%BB%8D
//以下代码写在配置类当中
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//增加一行 : 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
6.2、编写分页代码
new page对象,传入两个参数
当前页
页面大小
然后调用Mybatis-Plus的方法进行分页。
@Test
void pagenation(){
/**
* 创建page对象
* 传入两个参数:当前页和每页显示记录条数
*/
Page<User> page=new Page<>(1,3);
/**
* 使用方法:
* 调用Mybatis-plus的分页查询方法,进行底层的封装
* 将数据封装到page对象里面
*/
userMapper.selectPage(page,null);
/**
* 获取分页的当前页
*/
System.out.println(page.getCurrent());
/**
* 获取每页的list数据
*/
System.out.println(page.getRecords());
/**
* 获取表中的总记录数
*/
System.out.println(page.getTotal());
/**
* 获取总页数
*/
System.out.println(page.getPages());
/**
* 是否有下一页
*/
System.out.println(page.hasNext());
/**
* 是否有上一页
*/
System.out.println(page.hasPrevious());
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D48iUGqe-1690788258125)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220805162524854.png)]
@Bean public MybatisPlusInterceptor pageinationinterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } 查找问题的时候看到的,虽然跟我这个问题不是特别有关系,但是可能以后有用? https://blog.csdn.net/m0_49513507/article/details/124200458
7、性能分析插件
/**
* dev 开发环境
* test 测试环境
* prod 生产环境
*/
//该插件 3.2.0 以上版本移除推荐使用第三方扩展 执行SQL分析打印 功能
@Bean
@Profile({"dev","test"})
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor=new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//超过100msSQL语句不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
配置spring的环境:
在application.properties中添加:
spring.profiles.active=dev
8、实现复杂查询
@Test
void testWrapper(){
QueryWrapper<User> wrapper=new QueryWrapper<>();
//通过QueryWrapper设置条件
//ge,gt,le,lt 大于等于、大于、小于等于、小于
//示例:查询age>=30的
wrapper.ge("age",10);
//eq 等于 ne 不等于
wrapper.ne("name","lucy");
//between
wrapper.between("age",20,30);
//like 模糊查询
wrapper.like("email","test");
//oderByDesc
wrapper.orderByDesc("id");
//last 拼接到语句最后 尽量不要用
wrapper.last("limit 1");
//指定要查询的列
wrapper.select("id","name");
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdEUo5zi-1690788258130)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220806100639243.png)]
9、mybatis-plus实现自定义XML查询
首先写一个Mapper类继承BaseMapper类
项目目录结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r0pWlAik-1690788258134)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220806111820748.png)]
package com.study.mybatisplusstudy.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.study.mybatisplusstudy.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
/**
* @className: UserMapper
* @Author: yeyulemon
* @Date: 2022-08-05 10:06
**/
@Repository
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 也可以不用配置XML直接使用注解的方式实现
// @Select("select * from user where id = #{id}")
// User findSomeone(@Param("id") Long Id);
//声明一个方法findSomeone
User findSomeone(@Param("id") Long Id);
}
一定要记得在配置类或者是启动类加伤MapperScan,扫描mapper
@Configuration
@MapperScan("com/study/mybatisplusstudy/mapper")
public class MyConfig {
...
}
然后我们在resources文件夹下,创建一个文件夹,来存储我们的xml文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ur46o7sn-1690788258139)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220806111701212.png)]
还有就是在application.properties文件中,配置好对应的mapper.xml的位置,用于扫描mapper
#确定位置#
mybatis-plus.mapper-locations=classpath:/mybatisMapper/*Mapper.xml
#日志#
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mybatisplusstudy.mapper.UserMapper">
<resultMap id="userMap" type="com.study.mybatisplusstudy.entity.User">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="age" property="age" />
<result column="email" property="email" />
<result column="create_time" property="createTime" />
<result column="update_time" property="updateTime" />
<result column="version" property="version" />
<result column="deleted" property="deleted" />
</resultMap>
<select id="findSomeone" resultMap="userMap">
select id,name,age,email,create_time,update_time,version,deleted from user where id = ${id};
</select>
</mapper>
我们在xmlwen文件中一定要声明mapper的namespace,指定为刚刚创建的接口类(路径一定要选择正确)
然后是如果是查询语句,我们需要写一个对应的resultMap来指定查询结果中实体类与数据库中数据的对应关系
例如:数据库中查询出来的列名叫做create_time,找到实体类中对应的名称(一般是驼峰命名法)crateTime进行绑定
如上resultMap所示。
绑定完成之后,要在对应的select语句后面使用resultMap指定返回值的绑定关系。
之后,我们编写测试类来测试
/**
* discription 自定义XML方式实现SQL查询 <br/>
* author Macieserenity <br/> <br/>
* updateTime 2022/8/6 11:23 <br/>
* return <br/>
* throws <br/>
*/
@Test
void testXmlSQL(){
User selectUser =userMapper.findSomeone(1L);
System.out.println(selectUser);
}
不同于默认配置,这里的SQL日志打印出来之后是直接带着数据的,没有进行prepare预处理
预处理: where id = ?
这里没有进行预处理?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KiOY7g10-1690788258143)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220806112538208.png)]
原因是因为我们在xml中的select语句中使用的$符号而不是#号,此时我们将xml文件中的select语句进行修改:
<select id="findSomeone" resultMap="userMap">
select id,name,age,email,create_time,update_time,version,deleted from user where id = #{id};
</select>
注意,这里我们之前使用的是id = ${id} 此时我们将其改为 where id = #{id},再次执行测试类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iseyszhr-1690788258148)(%E5%B0%9A%E7%A1%85%E8%B0%B7-%E8%B0%B7%E7%B2%92%E5%AD%A6%E9%99%A2%E9%A1%B9%E7%9B%AE.assets/image-20220806112844194.png)]
可以看到,SQL语句正常的执行了预处理,其参数变成了 ?
返回的数据:
User(id=1, name=Jone, age=18, email=test1@baomidou.com, createTime=null, updateTime=null, version=null, deleted=0)