尚庭公寓租赁项目day03

6.2 技术储备

6.2.1 MyBatis Plus快速入门

6.2.2.1 概述

MyBatis-Plus(简称 MP)是一个MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。其突出的特性如下:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,提供了大量的通用的CRUD方法,因此可以省去大量手写sql的语句的工作。

  • 条件构造器:提供了强大的条件构造器,可以构造各种复杂的查询条件,以应对各种复杂查询。

  • 内置分页插件:配置好插件之后,写分页等同于普通 List 查询,无需关注分页逻辑。

下面通过一个简单案例快速熟悉MyBatis Plus的基本使用

6.2.2.2 数据库准备

首先在数据库中准备一张表,为后序的学习做准备。

  1. 创建数据库

    在MySQL中创建一个数据库hello_mp

    CREATE DATABASE hello_mp CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

  2. 创建表

    hello-mp库中创建一个表user

    DROP TABLE IF EXISTS user;
    CREATE TABLE user
    (
        id BIGINT(20) NOT NULL AUTO_INCREMENT 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)
    );

  3. 插入数据

    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');

6.2.2.3 与SpringBoot集成

Mybatis Plus与SpringBoot的集成十分简单,具体操作如下

  1. 引入Maven 依赖

    提前创建好一个SpringBoot项目,然后在项目中引入MyBatis Plus依赖

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.2</version>
    </dependency>

    本案例完整的pom.xml文件如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.0.9</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.atguigu</groupId>
        <artifactId>hello-mp</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>hello-mp</name>
        <description>hello-mp</description>
        <properties>
            <java.version>17</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    ​
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.5.3.2</version>
            </dependency>
        </dependencies>
    ​
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    ​
    </project>

  2. 配置application.yml文件

    配置数据库相关内容如下

    spring: datasource:   driver-class-name: com.mysql.cj.jdbc.Driver   username: root   password: Atguigu.123   url: jdbc:mysql://192.168.10.101:3306/hello_mp?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8
    
6.2.2.4 创建实体类

创建与user表相对应的实体类,如下

@Data
@TableName("user")
public class User {
​
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
​
    @TableField("name")
    private String name;
​
    @TableField("age")
    private Integer age;
​
    @TableField("email")
    private String email;
}

知识点

实体类中的三个注解的含义如下

  • @TableName:表名注解,用于标识实体类所对应的表

    • value:用于声明表名

  • @TableId:主键注解,用于标识主键字段

    • value:用于声明主键的字段名

    • type:用于声明主键的生成策略,常用的策略有AUTOASSIGN_UUIDINPUT等等

  • @TableField:普通字段注解,用于标识属性所对应的表字段

    • value:用于声明普通字段的字段名

6.2.2.4 通用Mapper

通用Mapper提供了通用的CRUD方法,使用它可以省去大量编写简单重复的SQL语句的工作,具体用法如下

  1. 创建Mapper接口

    创建UserMapper接口,并继承由Mybatis Plus提供的BaseMapper<T>接口,如下

    @Mapper
    public interface UserMapper extends BaseMapper<User> {
    }

    知识点

    若Mapper接口过多,可不用逐一配置@Mapper注解,而使用@MapperScan注解指定包扫描路径进行统一管理,例如

    @SpringBootApplication
    @MapperScan("com.atguigu.hellomp.mapper")
    public class HelloMpApplication {
    ​
        public static void main(String[] args) {
            SpringApplication.run(HelloMpApplication.class, args);
        }
    }

  2. 测试通用Mapper

    创建userMapperTest测试类型,内容如下

    @SpringBootTest
    class UserMapperTest {
    ​
        @Autowired
        private UserMapper userMapper;
    ​
        @Test
        public void testSelectList() {
            List<User> users = userMapper.selectList(null);
            users.forEach(System.out::println);
        }
    ​
        @Test
        public void testSelectById() {
            User user = userMapper.selectById(1);
            System.out.println(user);
        }
    ​
        @Test
        public void testInsert() {
            User user = new User();
            user.setName("zhangsan");
            user.setAge(11);
            user.setEmail("test@test.com");
            userMapper.insert(user);
        }
    ​
        @Test
        public void testUpdateById() {
            User user = userMapper.selectById(1);
            user.setName("xiaoming");
            userMapper.updateById(user);
        }
        
        @Test
        public void testDeleteById() {
            userMapper.deleteById(1);
        }
    }

6.2.2.5 通用Service

通用Service进一步封装了通用Mapper的CRUD方法,并提供了例如saveOrUpdatesaveBatch等高级方法。

  1. 创建Service接口

    创建UserService,内容如下

    public interface UserService extends IService<User> {
    }

  2. 创建Service实现类

    创建UserServiceImpl,内容如下

    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    }

  3. 测试通用Service

    创建UserServiceImplTest测试类,内容如下

    @SpringBootTest
    class UserServiceImplTest {
    ​
    ​
        @Autowired
        private UserService userService;
    ​
        @Test
        public void testSaveOrUpdate() {
            User user1 = userService.getById(2);
            user1.setName("xiaohu");
    ​
            User user2 = new User();
            user2.setName("lisi");
            user2.setAge(27);
            user2.setEmail("lisi@email.com");
            userService.saveOrUpdate(user1);
            userService.saveOrUpdate(user2);
        }
    ​
    ​
        @Test
        public void testSaveBatch() {
            User user1 = new User();
            user1.setName("dongdong");
            user1.setAge(49);
            user1.setEmail("dongdong@email.com");
    ​
            User user2 = new User();
            user2.setName("nannan");
            user2.setAge(29);
            user2.setEmail("nannan@email.com");
    ​
            List<User> users = List.of(user1, user2);
            userService.saveBatch(users);
        }
    }

6.2.2.6 条件构造器

条件构造器用于构造复杂的查询条件,例如获取name='zhangsan'的用户。MyBatis Plus共提供了两类构造器,分别是QueryWrapperUpdateWrapper。其中QueryWrapper主要用于查询、删除操作,UpdateWrapper主要用于更新操作,下面通过几个案例学习条件构造器的基础用法。

  1. 创建WrapperTest测试类,内容如下

    @SpringBootTest
    public class WrapperTest {
        @Autowired
        private Userservice userservice;
    
        @Test
        public void Querywrapper(){
            //查询name=Tom的所有用户
            QueryWrapper<User> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.eq("name","Billie");
    
            //查询邮箱域名为baomidou.com的所有用户
            QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
            queryWrapper2.like("email","baomidou.com");
    
            //查询所有用户信息并按照age字段降序排序
            QueryWrapper<User> queryWrapper3 = new QueryWrapper<>();
            queryWrapper3.orderByDesc("age");
    
            //查询age介于[20,30]的所有用户 le:小于等于  ge大于等于
            QueryWrapper<User> queryWrapper4 = new QueryWrapper<>();
            queryWrapper4.le("age",30).ge("age",20);
    
            //查询age小于20或大于30的用户
            QueryWrapper<User> queryWrapper5 = new QueryWrapper<>();
            queryWrapper5.lt("age",20).or().gt("age",30);
    
            //邮箱域名为baomidou.com且年龄小于30或大于40且的用户
            QueryWrapper<User> queryWrapper6 = new QueryWrapper<>();
            //进行化简
    //        queryWrapper6.like("email","baomidou.com").and(new Consumer<QueryWrapper<User>>() {
    //            @Override
    //            public void accept(QueryWrapper<User> userQueryWrapper) {
    //                userQueryWrapper.lt("age",30).or().gt("age",40);
    //            }
    //        });
    //        queryWrapper6.like("email","baomidou.com").and(userQueryWrapper -> userQueryWrapper.lt("age",30).or().gt("age",40));
    
            queryWrapper6.like("email","baomidou.com").and(a->a.lt("age",30).or().gt("age",40));
    
            //执行查询条件
            List<User> list = userservice.list(queryWrapper6);
            list.forEach(System.out::println);
        }
        @Test
        public void UpdateWrapper(){
            UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("name","Billie").set("email","tom@baomidou.com");
            userservice.update(updateWrapper);
        }
    }

  2. 创建LambdaWrapperTest测试类,内容如下

    上述的QueryWrapperUpdateWrapper均有一个Lambda版本,也就是LambdaQueryWrapperLambdaUpdateWrapperLambda版本的优势在于,可以省去字段名的硬编码,具体案例如下:

    @SpringBootTest
    public class LambdawrepperTest {
        @Autowired
        private Userservice userservice;
    
        @Test
        public void QueryWrapper(){
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(User::getName,"laozhao");
            List<User> list = userservice.list(lambdaQueryWrapper);
            list.forEach(System.out::println);
    
        }
    
        @Test
        public void UpdateWrapper(){
            LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            lambdaUpdateWrapper.eq(User::getName,"laozhao").set(User::getEmail,"Tom@tom.com");
            userservice.update(lambdaUpdateWrapper);
        }
    }

6.2.2.7 分页插件

分页查询是一个很常见的需求,故Mybatis-Plus提供了一个分页插件,使用它可以十分方便的完成分页查询。下面介绍Mybatis-Plus分页插件的用法,详细信息可参考官方文档

  • 配置分页插件

    创建com.atguigu.hellomp.config.MPConfiguration配置类,增加如下内容

    @Configuration
    public class MPConfiguration {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    }

  • 分页插件使用说明

    • 构造分页对象

      分页对象包含了分页的各项信息,其核心属性如下:

      属性名类型默认值描述
      recordsListemptyList查询数据列表
      totalLong0查询列表总记录数
      sizeLong10每页显示条数,默认10
      currentLong1当前页

      分页对象既作为分页查询的参数,也作为分页查询的返回结果,当作为查询参数时,通常只需提供currentsize属性,如下

      IPage<T> page = new Page<>(current, size);

      注:IPage为分页接口,PageIPage接口的一个实现类。

    • 分页查询

      Mybatis Plus的BaseMapperServiceImpl均提供了常用的分页查询的方法,例如:

      • BaseMapper的分页查询:

        IPage<T> selectPage(IPage<T> page,Wrapper<T> queryWrapper);

      • ServiceImpl的分页查询:

        // 无条件分页查询
        IPage<T> page(IPage<T> page);
        // 条件分页查询
        IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);

      • 自定义Mapper

        对于自定义SQL,也可以十分方便的完成分页查询,如下

        Mapper接口:

        IPage<UserVo> selectPageVo(IPage<?> page, Integer state);

        Mapper.xml

        <select id="selectPageVo" resultType="xxx.xxx.xxx.UserVo">
            SELECT id,name FROM user WHERE state=#{state}
        </select>

        注意Mapper.xml中的SQL只需实现查询list的逻辑即可,无需关注分页的逻辑。

  • 案例实操

    分页查询案例如下:

    创建PageTest测试类,内容如下

    @SpringBootTest
    public class PageTest {
        @Autowired
        private UserMapper userMapper;
        @Autowired
        private Userservice userService;
    
        // 测试分页userService中的
        @Test
        public void test1(){
            IPage<User> page = new Page<User>(2,3);
            IPage<User> result = userService.page(page);
            List<User> records = result.getRecords();
            records.forEach(System.out::println);
        }
    
        // 测试分页userMapper中的
        @Test
        public void test2(){
            IPage<User> page = new Page<User>(2,3);
            IPage<User> resuly = userMapper.selectPage(page, null);
            resuly.getRecords().forEach(System.out::println);
        }
    
        //测试自定义分页
        @Test
        public void test3(){
            IPage<User> page = new Page<User>(2,3);
            IPage<User> page1 = userMapper.selectUserPage(page);
            page1.getRecords().forEach(System.out::println);
        }
    }

    在UserMapper中声明分页查询方法如下

        IPage<User> selectUserPage(IPage<User> page);

    创建resources/mapper/UserMapper.xml文件,内容如下

    <?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="czk.hello_mp.mapper.UserMapper">
        <select id="selectUserPage" resultType="czk.hello_mp.entity.User">
            select *
            from user
        </select>
    </mapper>

    注意

    Mybatis-Plus中Mapper.xml文件路径默认为:classpath*:/mapper/**/*.xml,可在application.yml中配置以下参数进行修改

    mybatis-plus:
      mapper-locations: classpath*:/mapper/**/*.xml
6.2.2.8 MyBatisX插件

MyBatis Plus提供了一个IDEA插件——MybatisX,使用它可根据数据库快速生成EntityMapperMapper.xmlServiceServiceImpl等代码,使用户更专注于业务。

下面演示具体用法

  1. 安装插件

    在IDEA插件市场搜索MyBatisX,进行在线安装

  2. 配置数据库连接

    在IDEA中配置数据库连接

  3. 生成代码

    首先将之前编写的UserUserMapperUserServcieUserServiceImpl全部删除,然后按照下图指示使用插件生成代码

  4. 配置实体类相关信息
  5. 配置代码模版信息

  6. 点击Finish然后查看生成的代码。

6.2.2 MinIO快速入门

6.2.2.1 MinIO核心概念

下面介绍MinIO中的几个核心概念,这些概念在所有的对象存储服务中也都是通用的。

  • 对象(Object)

    对象是实际的数据单元,例如我们上传的一个图片。

  • 存储桶(Bucket)

    存储桶是用于组织对象的命名空间,类似于文件夹。每个存储桶可以包含多个对象。

  • 端点(Endpoint)

    端点是MinIO服务器的网络地址,用于访问存储桶和对象,例如http://192.168.10.101:9000

    注意:

    9000为MinIO的API的默认端口,前边配置的9001以为管理页面端口。

  • Access Key 和 Secret Key

    Access Key是用于标识和验证访问者身份的唯一标识符,相当于用户名。

    Secret Key是与Access Key关联的密码,用于验证访问者的身份。

6.2.2.2 MinIO管理页面操作
  1. 登录

    管理页面的地址为http://192.168.10.101:9001,登录的用户名和密码为部署时在EnvironmentFile文件中配置的如下参数

    MINIO_ROOT_USER=minioadmin
    MINIO_ROOT_PASSWORD=minioadmin
  2. 创建存储桶

  3. 上传图片

    • 找到目标桶

    • 上传图片

  4. 访问图片

    • 图片URL

      由于MinIO提供了HTTP访问功能,所以可以通过浏览器直接访问对象。对象URL为MinIO的Endpoint+对象的存储路径,例如下图中的图片对象的URL为http:192.168.10.101:9000/test/公寓-外观.jpg

    • 访问权限

      不出意外的话,使用浏览器访问上述URL,会得到如下响应,很显然是没有访问权限。

      <Error>
          <Code>AccessDenied</Code>
          <Message>Access Denied.</Message>
          <Key>公寓-外观.jpg</Key>
          <BucketName>test</BucketName>
          <Resource>/test/公寓-外观.jpg</Resource>
          <RequestId>177BC92022FC5684</RequestId>
          <HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId>
      </Error>

      若想继续访问图片,需要修改图片所在桶的访问权限,如下图所示

      如上图所示,可选的访问权限共有三个选项,分别是PrivatePublicCustom,具体说明如下

      • Private

        只允许桶的所有者对该桶进行读写。

      • Public

        允许所有人对该桶进行读写。

      • Custom

        自定义访问权限。

      若想将权限设置为只允许所有者写,但允许所有人读,就需要自定义访问权限。自定义访问权限,需要使用一个规定格式的JSON字符串进行描述,具体格式可参考官方文档

      例如以下JSON字符串表达的含义是:允许(Allow)所有人(*)读取(s3:GetObject)指定桶(test)的所有内容。

      {
        "Statement" : [ {
          "Action" : "s3:GetObject",
          "Effect" : "Allow",
          "Principal" : "*",
          "Resource" : "arn:aws:s3:::test/*"
        } ],
        "Version" : "2012-10-17"
      }
      

      test桶访问权限设置为Custom,并添加上述内容

      重新访问http:192.168.10.101:9000/test/公寓-外观.jpg,观察是否正常。

6.2.2.3 MinIO Java SDK

MinIO提供了多种语言的SDK供开发者使用,本项目需要用到Java SDK,下面通过一个简单案例熟悉一下其基本用法,具体内容可参考官方文档

  1. 创建一个Maven项目

  2. 引入如下依赖

    <!--        引入minio依赖-->
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>8.5.3</version>
            </dependency>

  3. 编写如下内容

    public class hellominio {
        public static void main(String[] args) {
            //引入minio
            String endpoint = "http://192.168.254.138:9000";
            String accessKey = "minioadmin";
            String secretKey = "minioadmin";
            String bucketName = "hello-minio";
    
            MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
    
    
            try {
                //判断是否存在这个桶
                boolean b = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
                if (!b){
                    //创建存储桶
                    minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
    
                    //开始设置配置
                    //使用JAVA新特性三双引号,可以保留字符串的格式
                    // %s是占位符,后面是参数
                    String policy = """
                            {
                              "Statement" : [ {
                                "Action" : "s3:GetObject",
                                "Effect" : "Allow",
                                "Principal" : "*",
                                "Resource" : "arn:aws:s3:::%s/*"
                              } ],
                              "Version" : "2012-10-17"
                            }
                            """.formatted(bucketName);
                    minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build());
    
    
    
                }
                //上传文件
                minioClient.uploadObject(UploadObjectArgs.builder().filename("C:\\Users\\20724\\Desktop\\FileTest\\a.txt").bucket(bucketName).object("a.txt").build());
                System.out.println("上传成功");
    
            } catch (Exception e) {
                System.err.println("发生错误: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }
    

  4. 运行测试

    运行上述代码,然后查看MinIO管理页面,观察是否上传成功。

6.2.3 Redis快速入门

6.2.3.1 Redis概述
  1. Reids定义

    Redis(Remote Dictionary Server)是一个基于内存的键值对存储系统,常用作缓存服务。由于Reids将数据都保存在内存中,因此其读写性能十分惊人,同时,为保证数据的可靠性,Redis会将数据备份到硬盘上,用于故障发生时的数据恢复。

  2. Redis特点

    • 高性能:Redis主要将数据存储在内存中,因此读写速度非常快,适合对速度有较高要求的场景。

    • 支持多种数据结构:Redis中键值对的值(Value)支持多种数据结构,如字符串、哈希表、列表、集合等,这使得它可以应用于多种不同的场景。

    • 持久化:Redis可以通过定期快照或者实时记录写操作日志的方式将内存中的数据持久化到硬盘,确保数据在重启后不会丢失。

    • 灵活的数据过期策略:可以为每个键设置过期时间,一旦过期,Redis会自动删除。

  3. Redis应用场景

    Redis最为常见的一个应用场景就是用作缓存,缓存可以显著提升访问速度,降低数据库压力。

6.2.3.2 Reids客户端
  1. 命令行客户端

    Reids提供了一个命令行客户端redis-cli,我们可以使用它连接到Redis服务器,然后通过命令与服务器进行各种交互,例如数据的增删改查。下面介绍该命令的基本用法。

    • 启动客户端

      redis-cli -h 127.0.0.1 -p 6379

      说明

      • -h <hostname>选项用于声明Redis服务器的主机名或IP地址,默认值为127.0.0.1

      • -p <port>选项用于声明Redis服务器监听的端口号,默认值为6379

    • 测试连接状态

      ping命令可用于测试连接状态,语法如下

      ping

      说明:若连接正常,则会返回pong。

    • 退出客户端

      quit命令可用于断开客户端与服务端的连接,并退出客户端,语法如下

       quit
  2. 图形化客户端

    Redis官方推荐的图形化客户端为RedisInsight,该客户端开源免费,且功能强大,下面演示如何使用。

    • 安装

      安装包为RedisInsight-v2-win-installer.exe,可在项目资料中获取,安装步骤比较简单,大家自行安装即可。

    • 选择配置数据库连接

    • 填写连接信息,并测试连接,通过后保存连接

    • 点击创建好的连接

    • 工作界面如下

6.2.3.3 Redis常用数据类型及命令
6.2.3.3.1 通用命令
  • 查看所有键

    keys命令可用于查看所有键,语法如下

    keys pattern

    说明:pattern用于匹配key,其中*表示任意个任意字符,?表示一个任意字符。

    示例:

    127.0.0.1:6379> KEYS *
    1) "k3"
    2) "k2"
    3) "k1"

    注意:该命令会遍历Redis服务器中保存的所有键,因此当键很多时会影响整个Redis服务的性能,线上环境需要谨慎使用。

  • 键总数

    dbsize可用于查看键的总数,语法如下

    dbsize
  • 判断键是否存在

    exists命令可用于判断一个键是否存在,语法如下

    exists key

    说明:若键存在则返回1,不存在则返回0。

  • 删除键

    del可用于删除指定键,语法如下

    del key [key ...]

    说明:返回值为删除键的个数,若删除一个不存在的键,则返回0。

  • 查询键的剩余过期时间

    ttl key

    说明ttl的含义为time to live,用于查询一个定时键的剩余存活时间,返回值以秒为单位。若查询的键的未设置过期时间,则返回-1,若查询的键不存在,则返回-2

  • 数据库管理命令

    Redis默认有编号为0~15的16个逻辑数据库,每个数据库之间的数据是相互独立的,所有连接默认使用的都是0号数据库。

    • 切换数据库

      select命令可用于切换数据库,语法如下

      select index

      说明:若index超出范围,会报错

    • 清空数据库

      flushdb命令会清空当前所选用的数据库,flushall命令会清空0~15号所有的数据库。

      注意:生产环境慎用

6.2.3.3.2 string类型
  1. 概述

    Redis中的string类型保存的是字节序列(Sequence of bytes),因此任意类型的数据,只要经过序列化之后都可以保存到Redis的string类型中,包括文本、数字甚至是一个对象。

  2. 常用命令

    • set

      set命令用于添加string类型的键值对,具体语法如下

      SET key value [NX|XX] [EX seconds|PX milliseconds]

      各选项含义如下

      • NX:仅在key不存在时set

      • XX:仅在key存在时set

      • EX seconds:设置过期时间,单位为秒

      • PX milliseconds:设置过期时间,单位为毫秒

    • get

      get命令用于获取某个string类型的键对应的值,具体语法如下

      GET key
    • incr

      incr命令用于对数值做自增操作,具体语法如下

      INCR key

      若key对应的value是整数,则返回自增后的结果,若不是整数则报错,若key不存在则创建并返回1。

    • decr

      decr命令用于对数值做自减操作,具体语法如下

      DECR key

      若key对应的value是整数,则返回自减后的结果,若不是整数则报错,若key不存在则创建并返回-1。

  3. 应用场景

    string类型常用于缓存、计数器等场景。

6.2.3.3.3 list类型
  1. 概述

    list类型可用于存储多个string类型的元素,并且所有元素按照被添加的顺序存储。

  2. 常用命令

    list类型相关的命令较多,下面分类进行进行介绍。

    • 添加元素

      向列表中添加元素的命令有lpushrpushlinsert,各命令的功能与用法如下

      • lpush

        该命令用于向list左侧添加元素,语法如下

        lpush key element [element ...]

        示例

        lpush l1 a b c
      • rpush

        该命令用于向list右侧添加元素,语法如下

        rpush key element [element ...]
      • linsert

        该命令用于向list指定位置添加元素,语法如下

        linsert key before|after pivot element

        示例

        linsert l1 after b new
    • 查询元素

      查询list元素的命令有lindexlrange,各命令的功能与用法如下

      • lindex

        该命令用于获取指定索引位置的元素,语法如下

        lindex key index

        说明:index从左到右依次是0,1,2...,从右到左依次是-1,-2,-3...

      • lrange

        该命令用于获取指定范围内的元素列表,语法如下

        lrange key start stop

        示例

        获取list全部元素,命令如下

        lrange l1 0 -1
    • 删除元素

      删除list元素的命令有lpoprpoplrem,各命令的功能与用法如下

      • lpop

        该命令用于移除并返回list左侧元素,语法如下

        lpop key [count]

        说明:count参数表示移除元素的个数

      • rpop

        该命令用于移除并返回list右侧的元素,语法如下

        rpop key [count]
      • lrem

        该命令用于移除list中的指定元素,语法如下

        lrem key count element

        说明:count参数表示要移除element元素的个数(list中可以存在多个相同的元素),count的用法如下

        • 若count>0,则从左到右删除最多count个element元素

        • 若count<0,则从右到左删除最多count(的绝对值)个element元素

        • 若count=0,则删除所有的element元素

    • 修改元素

      lset命令可用于修改指定索引位置的元素,语法如下

      lset key index element
    • 其他

      llen命令可用于查看list长度,语法如下

      llen key
  3. 应用场景

    • 社交应用中,可使用list缓存每个用户发布的最新的N条记录。

    • list可用作异步消息队列。

6.2.3.3.4 set类型
  1. 概述

    和list类型相似,set类型也可用来存储多个string类型的元素,但与list类型不同,set中的元素是无序的,且set中不会包含相同元素。

  2. 常用命令

    • 集合内

      • sadd

        该命令用于向set中添加元素,语法如下

        sadd key member [member ...]
 - **smembers**
​
   该命令用于查询set中的全部元素,语法如下
​
   ```
   smembers key
   ```
 - **srem**
​
   该命令用于移除set中的指定元素,语法如下
​
   ```
   srem key member [member ...]
   ```
 - **spop**
​
   该命令随机移除并返回set中的n个元素,语法如下
​
   ```
   spop key [count]
   ```
 - **srandmember**
​
   该命令随机返回set中的n个元素(不删除),语法如下
​
   ```
   srandmember key [count]
   ```
 - **scard**(Cardinality,基数)
​
   该命令用于查询set中的元素个数,语法如下
​
   ```
   scard key
   ```
 - **sismember**
​
   该命令用于元素是否在set中,语法如下
​
   ```
   sismember key element
   ```
  • 集合间

    • sinter

      该命令用于计算多个集合的交集,语法如下

      sinter key [key ...]
    • sunion

      该命令用于计算多个集合的并集,语法如下

      sunion key [key ...]
    • sdiff

      该命令用于计算多个集合的差集,语法如下

      sdiff key [key ...]
  1. 应用场景

    set可用于计算共同关注好友,随机抽奖系统等等。

6.2.3.3.5 hash类型
  1. 概述

    hash类型类似于Java语言中的HashMap,可用于存储键值对。

  2. 常用命令

    • hset

      该命令用于向hash中增加键值对,语法如下

      hset key field value [field value ...]
    • hget

      该命令用于获取hash中某个键对应的值,语法如下

      hget key field
    • hdel

      该命令用于删除hash中的指定的键值对,语法如下

      hdel key field [field ...]
    • hlen

      该命令用于查询hash中的键值对个数,语法如下

      hlen key
    • hexists

      该命令用于判断hash中的某个键是否存在,语法如下

      hexists key field
    • hkeys

      该命令用于返回hash中所有的键,语法如下

      hkeys key
    • hvals

      该命令用于返回hash中所有的值,语法如下

      hvals key
    • hgetall

      该命令用于返回hash中所有的键与值,语法如下

      hgetall key
  3. 应用场景

    hash类型可用于缓存对象等。

6.2.3.3.6 zset类型
  1. 概述

    zset(sorted set)被称为有序集合,同set相似,zset中也不会包含相同元素,但不同的是,zset中的元素是有序的。并且zset中的元素并非像list一样按照元素的插入顺序排序,而是按照每个元素的分数(score)排序。

  2. 常用命令

    • zadd

      该命令用于向zset中添加元素,语法如下

      ZADD key [NX|XX] score member

      说明:

      • NX:仅当member不存在时才add

      • XX:仅当member存在时才add

    • zcard

      该命令用于计算zset中的元素个数,语法如下

      zcard key
    • zscore

      改名用于查看某个元素的分数,语法如下

      zscore key member
    • zrank/zrevrank

      这组命令用于计算元素的排名,其中zrank按照score的升序排序,zrevrank则按照降序排序,语法如下

      zrank/zrevrank key member

      说明:名次从0开始。

    • zrem

      该命令用于删除元素,语法如下

      zrem key member [member ...]
    • zincrby

      该命令用于增加元素的分数,语法如下

      zincrby key increment member
    • zrange

      该命令用于查询指定区间范围的元素,语法如下

      zrange key start stop [byscore] [rev] [limit offset count] [withscores]

      说明:

      • start/stop:用于指定查询区间,但是在不同模式下,其代表的含义也不相同

        • 默认模式下,start~stop表示的是名次区间,且该区间为闭区间。名次从0开始,且可为负数,-1表示倒数第一,-2表示倒数第二,以此类推。

        • byscore模式下(声明了byscore参数),则start~stop表示的就是分数区间,该区间默认仍为闭区间。在该模式下,可以在startstop前增加(来表示开区间,例如(1 (5,表示的就是(1,5)这个开区间。除此之外,还可以使用-inf+inf表示负无穷和正无穷。

      • byscore:用于切换到分数模式

      • rev:表示降序排序。在byscore模式下使用rev参数需要注意查询区间,start应大于stop。

      • limit:该选项只用于byscore模式,作用和sql语句中的limit一致

      • withscores:用于打印分数

  3. 应用场景

    zset主要用于各种排行榜。

6.2.3.4 SpringBoot整合Redis
6.2.3.4.1 Spring Data Redis概述

Spring Data Redis 是Spring大家族中的一个子项目,主要用于Spring程序和Redis的交互。它基于的Redis Java客户端(JedisLettuce)做了抽象,提供了一个统一的编程模型,使得Spring程序与Redis的交互变得十分简单。

Spring Data Redis 中有一个十分重要的类——RedisTemplate,它封装了与Redis进行的交互的各种方法,我们主要用使用它与Redis进行交互。

6.2.3.4.2 Spring Data Redis快速入门
  1. 创建SpringBoot项目

  2. 引入Maven依赖

    Spring Boot提供了对Spring Data Redis的支持,在Spring Boot项目中可以直接引入spring-boot-starter-data-redis来完成Spring Data Redis的自动配置,具体依赖如下

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

  3. 配置application.yml文件

    application.yml文件中增加如下参数

    spring:
      data:
        redis:
          host: 192.168.10.101
          port: 6379
          database: 0
    
  4. RedisTemplate使用

    由于spring-boot-starter-data-redis中提供了RedisTemplate的自动配置,所以我们可以将RedisTemplate注入自己的类中,如下边的案例所示

    @SpringBootTest
    public class TestRedisTemplate {
    ​
        @Autowired
        private RedisTemplate redisTemplate;
    ​
        @Test
        public void testRedisTemplate() {
    ​
        }
    }

    根据Redis的数据类型,RedisTemplate对各种交互方法做了分组,以下是常用的几个分组

    分组说明
    redisTemplate.opsForValue()操作string类型的方法
    redisTemplate.opsForList()操作list类型的方法
    redisTemplate.opsForSet()操作set类型的方法
    redisTemplate.opsForHash()操作hash类型的方法
    redisTemplate.opsForZSet()操作zset类型的方法
    redisTemplate通用方法

    下面简单测试几个简单的方法

    @SpringBootTest
    public class TestRedisTemplate {
    ​
        @Autowired
        private RedisTemplate redisTemplate;
    ​
        @Test
        public void testSet() {
            redisTemplate.opsForValue().set("key1", "value1");
        }
    ​
        @Test
        public void testGet() {
            String result = (String) redisTemplate.opsForValue().get("key1");
            System.out.println(result);
        }
    ​
        @Test
        public void testDel() {
            redisTemplate.delete("key1");
        }
    }
  5. 序列化问题

    • 问题演示

      • 问题一

        使用RedisTemplate向Redis中增加一个键值对

        redisTemplate.opsForValue().set("key2","value2");

        使用RedisTemplate查询key2所对应的value,有结果

        redisTemplate.opsForValue().get("key2");

        使用命令行客户端查询key2所对应的value,无结果

        get key2
      • 问题二

        在图形化客户端或者命令行客户端观察key2,显示异常

        RedisInsight中的key2显示如下

        命令行客户端中的key2显示如下

    • 问题说明

      上述问题的根本原因是,Redis中的key和value均是以二进制的形式存储的,因此客户端输入的key和value都会经过序列化之后才发往Redis服务端。而RedisTemplate所使用序列化方式和命令行客户端采用序列化方式不相同,进而导致序列化之后的二进制数据不同,所以才会导致上述的现象。

  6. StringRedisTemplate使用

    为解决上述问题,可使用StringRedisTemplate代替RedisTemplate,因为StringRedisTemplate使用的序列化器和命令行所使用的序列化器是相同的。

    spring-boot-starter-data-redis同样提供了StringRedisTemplate的自动配置,因此我们也可以直接将其注入到自己的类中。实例代码如下

    @SpringBootTest
    public class TestStringRedisTemplate {
    ​
        @Autowired
        private StringRedisTemplate redisTemplate;
    ​
        @Test
        public void testSet() {
            redisTemplate.opsForValue().set("key4", "value4");
        }
    ​
        @Test
        public void testGet() {
            String result = redisTemplate.opsForValue().get("key4");
            System.out.println(result);
        }
    ​
        @Test
        public void testDel() {
            redisTemplate.delete("key4");
        }
    }

6.2.4 Knife4j快速入门

6.2.4.1 概述

Knife4j是一个用于生成和展示API文档的工具,同时它还提供了在线调试的功能,下图是其工作界面。

了解

  • Knife4j有多个版本,最新版的Knife4j基于开源项目springdoc-openapi,这个开源项目的核心功能就是根据SpringBoot项目中的代码自动生成符合OpenAPI规范的接口信息。

  • OpenAPI规范定义接口文档的内容和格式,其前身是Swagger规范。

6.2.4.2 与SpringBoot集成

与SpringBoot的集成相对简单,具体操作如下

  1. 创建SpringBoot项目

  2. 引入Maven 依赖

    Knife4j的依赖如下

    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
        <version>4.3.0</version>
    </dependency>

    项目完整的pom.xml文件如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.0.9</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.atguigu</groupId>
        <artifactId>hello-knife4j</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>hello-knife4j</name>
        <description>hello-knife4j</description>
        <properties>
            <java.version>17</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    ​
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    ​
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
                <version>4.3.0</version>
            </dependency>
        </dependencies>
    ​
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
  3. 创建配置类

    创建com.atguigu.helloknife4j.config.Knife4jConfiguration,内容如下

    @Configuration
    public class Knife4jConfiguration {
    ​
        @Bean
        public OpenAPI openAPI() {
            return new OpenAPI()
                    .info(new Info()
                            .title("hello-knife4j项目API")
                            .version("1.0")
                            .description("hello-knife4j项目的接口文档"));
        }
        
        @Bean
        public GroupedOpenApi userAPI() {
            return GroupedOpenApi.builder().group("用户信息管理").
                    pathsToMatch("/user/**").
                    build();
        }
    ​
        @Bean
        public GroupedOpenApi systemAPI() {
            return GroupedOpenApi.builder().group("产品信息管理").
                    pathsToMatch("/product/**").
                    build();
        }
    }
  4. 启动项目

    启动SpringBoot项目,访问http://localhost:8080/doc.html,观察接口文档。

6.2.4.3 基本使用

Knife4j的使用也十分简单,我们只需使用几个简单注解,对接口进行描述,Knife4j就能自动生成API文档了。具体操作如下

  1. 描述实体类

    创建com.atguigu.helloknife4j.entity.User,内容如下

    @Data
    @Schema(description = "用户信息实体")
    public class User {
    ​
        @Schema(description = "编号")
        private Long id;
    ​
        @Schema(description = "用户姓名")
        private String name;
    ​
        @Schema(description = "用户年龄")
        private Integer age;
    ​
        @Schema(description = "用户邮箱")
        private String email;
    }

    知识点

    @Schema注解用于描述作为接口参数或者返回值的实体类的数据结构。

  2. 描述Controller接口

    创建com.atguigu.helloknife4j.controller.HelloController,内容如下

    @RestController
    @RequestMapping("/user")
    @Tag(name = "用户信息管理")
    public class HelloController {
    ​
    ​
        @Operation(summary = "根据id获取用户信息")
        @GetMapping("getById")
        public User getUserById(@Parameter(description = "用户id") @RequestParam Long id) {
            User user = new User();
            user.setId(id);
            user.setName("zhangsan");
            user.setAge(11);
            user.setEmail("zhangsan@email.com");
            return user;
        }
    }

    知识点

    @Tag注解用于对接口进行分类,相同Tag的接口会放在同一个菜单。

    @Operation用于对接口进行描述。

    @Parameter用于对HTTP请求参数进行描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值