谷粒学院--在线教育实战项目【一】

news2024/12/24 22:12:47

谷粒学院--在线教育实战项目【一】

  • 一、项目概述
    • 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 表

表结构

idnameageemail
1Jone18test1@baomidou.com
2Jack20test2@baomidou.com
3Tom28test3@baomidou.com
4Sandy21test4@baomidou.com
5Billie24test5@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 -V

SELECT 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 {

    @Autowired
    private UserMapper userMapper;
    
    @Test
    public 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】

    //添加数据操作
    @Test
    public 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=?

    //修改操作
    @Test
    public 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_timeupdate_time

-- 删除字段
ALTER TABLE user drop createTime,drop updateTime;
-- 删除单个字段 alter table 表名称 drop 字段;
-- 删除多个字段 alter table 表名称 drop 字段1,drop 字段2,drop 字段3;

-- 添加字段
ALTER TABLE user
        ADD 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.测试添加数据

    //添加用户
    @Test
    public 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实现添加操作,这个方法执行
    @Override
    public 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实现修改操作,这个方法执行
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());

    }
}

4.4.7.测试添加数据

    //添加用户
    @Test
    public 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默认值

    @Override
    public 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 {

    /**
     * 乐观锁插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

4.5.5.测试乐观锁可以修改成功

测试后分析打印的sql语句,将version的数值进行了加1操作

    //测试 乐观锁插件 添加数据
    @Test
    public 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自动回填
    }
    
    //测试 乐观锁插件
    @Test
    public 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.测试乐观锁修改失败

    //测试乐观锁插件 失败
    @Test
    public void testOptimisticLockerFail() {
        //查询
        User user = userMapper.selectById(1765668893893959681L);
        System.out.println(user);
        //修改数据
        user.setName("lisi1");
        user.setEmail("lisi@qq.com1");
        //模拟取出数据后,数据库中version实际数据比取出的值大,即已被其它线程修改并更新了version
        user.setVersion(user.getVersion() - 1);
        //执行更新
        userMapper.updateById(user);
        System.out.println(user);
    }

在这里插入图片描述

4.6.select【查询】

4.6.1.根据id查询记录

    //根据id查询记录
    @Test
    public void testSelectById() {
        User user = userMapper.selectById(1L);
        System.out.println("user = " + user);
    }

在这里插入图片描述

4.6.2.通过多个id批量查询

    //通过多个id批量查询
    //完成了动态sql的foreach的功能
    @Test
    public void testSelectBatchIds() {
        List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        userList.forEach(System.out::println);
    }

在这里插入图片描述

4.6.3.简单的条件查询

    //简单的条件查询
    //通过map封装查询条件
    @Test
    public void testSelectByMap() {
        HashMap<String, Object> hashMap = new HashMap<>();

        //注意:map中的key对应的是数据库中的列名。
        //例如数据库user_id,实体类是userId,这时map的key需要填写user_id
        hashMap.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 {

    @Bean
    public 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对象获取相关数据

    //分页查询
    @Test
    public 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
    @Test
    public 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删除记录
    @Test
    public void testDeleteById() {
        int result = userMapper.deleteById(1L);
        System.out.println(result);
    }

在这里插入图片描述
在这里插入图片描述

4.7.2.批量删除

    //批量删除
    @Test
    public void testDeleteBatchIds() {
        int result = userMapper.deleteBatchIds(Arrays.asList(2, 3, 4));
        System.out.println(result);
    }

在这里插入图片描述
在这里插入图片描述

4.7.3.简单的条件查询删除

    //简单的条件查询删除
    @Test
    public 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默认值

    @Override
    public 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,才能被选取出来执行逻辑删除的操作
    //测试 逻辑删除
    @Test
    public void testLogicDelete() {
        int result = userMapper.deleteById(5L);
        System.out.println(result);
    }

在这里插入图片描述
在这里插入图片描述

4.8.6.测试逻辑删除后的查询

MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断

    //测试 逻辑删除后的查询:不包括被逻辑删除的记录
    @Test
    public 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 {

    /*@Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
        //interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType

        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }*/

    //乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }

    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    //逻辑删除插件
    @Bean
    public 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实现添加操作,这个方法执行
    @Override
    public 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实现修改操作,这个方法执行
    @Override
    public 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.propertiesapplication-test.propertiesapplication-prod.properties
也可以自定义环境名称:如test1、test2

5.2.测试

5.2.1.常规测试

/**
 * Description:测试性能
 */
@SpringBootTest
public class Perform {
    @Autowired
    private UserMapper userMapper;

    //测试 性能分析插件
    @Test
    public 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

endl 小技巧:护眼模式:CAE6CA、C7EDCC

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1498569.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vcruntime140.dll丢失的修复办法详细介绍以及详细步骤

当电脑丢失vcruntime140.dll文件时&#xff0c;电脑会出现关于vcruntime140.dll丢失的错误提示&#xff0c;vcruntime140.dll文件包含许多重要的函数和资源&#xff0c;若缺少或丢失该文件&#xff0c;可能会导致电脑出现异常状况。今天就来和大家说说如果电脑出现关于vcruntim…

经验分享:水牛社怎么做?

本人也就是通过他慢慢学习成长起来的。还是一个网友推荐的&#xff0c;现在他对我来说算是大佬了&#xff0c;已经单飞了&#xff0c;好久都没有联系了&#xff0c;呵呵&#xff0c;真是人往高处走&#xff0c;水往低处流啊。 做网赚会经常和一些网络小白聊天&#xff0c;聊着…

Android视角看鸿蒙第三课(module.json中的各字段含义之nametype)

Android视角看鸿蒙第三课(module.json中的各字段含义) 前言 上篇文章我们试图找到鸿蒙app的程序入口&#xff0c;确定了在鸿蒙工程中,由AppScope下的app.json5负责应用程序的图标及名称,由entry->src->main-module.json5负责桌面图标及名称的展示。 AppScope下的app.js…

IAR全面支持小华全系芯片,强化工控及汽车MCU生态圈

IAR Embedded Workbench for Arm已全面支持小华半导体系列芯片&#xff0c;加速高端工控MCU和车用MCU应用的安全开发 嵌入式开发软件和服务的全球领导者IAR与小华半导体有限公司&#xff08;以下简称“小华半导体”&#xff09;联合宣布&#xff0c;IAR Embedded Workbench fo…

HTML超详细简介

HTML是什么 超文本标记语言&#xff08;HyperText Mark-up Language &#xff09;用来设计网页的标记语言用该语言编写的文件&#xff0c;以 .html或 .htm为后缀由浏览器解释执行不区分大小写&#xff0c;建议小写 HTML标签 HTML用于描述功能的符号成为“标签”标签都封装在…

如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?

原文链接&#xff1a;如何用ChatGPTGEEENVIPython进行高光谱&#xff0c;多光谱成像遥感数据处理&#xff1f; 第一&#xff1a;遥感科学 从摄影侦察到卫星图像 遥感的基本原理 遥感的典型应用 第二&#xff1a;ChatGPT ChatGPT可以做什么&#xff1f; ChatGPT演示使用 …

专访|云安全攻防:从理论到应用的全面探索

2023年11月&#xff0c;美国核研究实验室&#xff08;INL&#xff09;遭遇数据泄露。同年10月&#xff0c;索尼的员工数据在MOVEit攻击事件中被泄露。2024年2月&#xff0c;某知名制造商因云存储服务器的配置错误导致了敏感数据泄露。 这些事件表示企业必须重视云安全建设&…

校园小情书微信小程序,社区小程序前后端开源,校园表白墙交友小程序

功能 表白墙卖舍友步数旅行步数排行榜情侣脸漫画脸个人主页私信站内消息今日话题评论点赞收藏 效果图

[OpenCv]频域处理

目录 前言 一、频域变换 1.傅里叶变换 2.代码实现 二、频域中图像处理 1.理解数字图片的频谱 2.频域图像处理步骤 3.使用低通滤波器实现图像平滑 4.使用高通滤波器实现图像锐化 三、总结 前言 数字图像处理的方法有两大类&#xff1a;一种是空间域处理法&#xff0c;…

Pytorch学习 day06(torchvision中的datasets、dataloader)

torchvision的datasets 使用torchvision提供的数据集API&#xff0c;比较方便&#xff0c;如果在pycharm中下载很慢&#xff0c;可以URL链接到迅雷中进行下载&#xff08;有些URL链接在源码里&#xff09;代码如下&#xff1a; import torchvision # 导入 torchvision 库 # …

Mac版2024 CleanMyMac X 4.14.6 核心功能详解以及永久下载和激活入口

CleanMyMac 是 macOS 上久负盛名的系统清理工具&#xff0c;2018 年&#xff0c;里程碑式版本 CleanMyMac X 正式发布。不仅仅是命名上的变化&#xff0c;焕然一新的 UI、流畅的动画也让它显得更加精致。新增的系统优化、软件更新等功能&#xff0c;使得在日常使用 macOS 时有了…

C# Mel-Spectrogram 梅尔频谱

目录 介绍 Main features Philosophy of NWaves 效果 项目 代码 下载 C# Mel-Spectrogram 梅尔频谱 介绍 利用NWaves实现Mel-Spectrogram 梅尔频谱 NWaves github 地址&#xff1a;https://github.com/ar1st0crat/NWaves NWaves is a .NET DSP library with a lot …

Springboot 的几种配置文件形式

方式一&#xff1a;多个yml文件 步骤1&#xff1a;创建多个配置文件 application.yml #主配置文件 application-dev.yml #开发环境的配置 application-prod.yml #生产环境的配置 application-test.yml #测试环境的配置步骤2&#xff1a;applicaiton.yml中指定配置 在a…

H3C BGP 基本配置实验

H3C BGP 基本配置实验 实验拓扑 ​​ 实验需求 按照图示配置 IP 地址&#xff0c;R1 和 R5 上使用环回口模拟业务网段&#xff0c;R2&#xff0c;R3&#xff0c;R4 的环回口用于配置 Router-id 和建立 IBGP 邻居AS 200 运行 OSPF 实现内部网络互通R1&#xff0c;R2&#xf…

Python 中实现 CDF 累积分布图的两种方法

什么是累积分布 累积分布函数&#xff0c;又叫分布函数&#xff0c;是概率密度函数的积分&#xff0c;能完整描述一个实随机变量X的概率分布。一般以大写“CDF”&#xff08;Cumulative Distribution Function&#xff09;标记。 《百度百科》 累积分布函数&#xff0c;又叫分…

LeetCode 刷题 [C++] 第300题.最长递增子序列

题目描述 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 题目…

【Linux】Linux原生异步IO(一):libaio-介绍

1、IO模型 1.1 简述 相信大家在搜索的时候,都会看到下面这张图,IO的使用场景:同步、异步、阻塞、非阻塞,可以组合成四种情况: 同步阻塞I/O: 用户进程进行I/O操作,一直阻塞到I/O操作完成为止。同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONBLOCK,I/O操作可…

什么样的项目适合Web自动化测试

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

共用体union

一、共用体的特性 共用体又叫做联合体&#xff0c;共用体的特性如下&#xff1a; 1.共用体的所有成员共用一段内存空间&#xff0c;且所有成员的起始位置是一致的 2.共用体的值由最后赋值的成员决定 3.共用体的内存大小 共用体的内存必须大于或等于其他成员变量中最大数据类型…

专题1 - 双指针 - leetcode 15. 三数之和 - 中等难度

leetcode 15. 三数之和 - 点击直达 leetcode 15. 三数之和 中等难度 双指针1. 题目详情1. 原题链接2. 基础框架 2. 解题思路1. 题目分析2. 算法原理3. 时间复杂度 3. 代码实现4. 知识与收获 leetcode 15. 三数之和 中等难度 双指针 1. 题目详情 给你一个整数数组 nums &#…