主键和分页和ActiveRecord和SimpleQuery

奋斗吧
奋斗吧
擅长邻域:未填写

标签: 主键和分页和ActiveRecord和SimpleQuery Java博客 51CTO博客

2023-07-29 18:24:05 190浏览

主键和分页和ActiveRecord和SimpleQuery,1. 主键策略  761.1  主键生成策略介绍   76首先大家先要知道什么是主键,主键的作用就是唯一标识,我们可以通过这个唯一标识来定位到这条数据。当然对于表数据中的主键,我们可以自己设计生成规则,生成主键。但是在更多的场景中,没有特殊要求的话,我们每次自己手动生成的比较麻烦,我们可以借助框架提供好的主键生成策略,

1. 主键策略  76

1.1  主键生成策略介绍   76

首先大家先要知道什么是主键,主键的作用就是唯一标识,我们可以通过这个唯一标识来定位到这条数据。

当然对于表数据中的主键,我们可以自己设计生成规则,生成主键。但是在更多的场景中,没有特殊要求的话,我们每次自己手动生成的比较麻烦,我们可以借助框架提供好的主键生成策略,来生成主键。这样比较方便快捷

在MybatisPlus中提供了一个注解,是@TableId,该注解提供了各种的主键生成策略,我们可以通过使用该注解来对于新增的数据指定主键生成策略。那么在以后新增数据的时候,数据就会按照我们指定的主键生成策略来生成对应的主键。

1.2  AUTO策略  76

该策略为跟随数据库表的主键递增策略,前提是数据库表的主键要设置为自增

主键和分页和ActiveRecord和SimpleQuery_主键

主键和分页和ActiveRecord和SimpleQuery_分页_02

此处要设置好下次递增的数字

主键和分页和ActiveRecord和SimpleQuery_分页_03

实体类添加注解,指定主键生成策略

主键和分页和ActiveRecord和SimpleQuery_User_04

package com.powernode.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//数据库对应的实体类user   4
@Data  //生成set get方法
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
//@TableName("powershop_user") //指定数据库中的powershop_user表和这个User实体类映射  30
public class User {

    @TableId(type = IdType.AUTO) //指定id的自增策略 (跟随我们数据库的主键自增策略来的) 77
    private Long id;

//    @TableField("username") //指定表中的字段名和属性对应  32
    private String name;

//    @TableField(select = false)//时age字段失效查询不到  34
    private Integer age;

    private String email;

//    @TableField("`desc`") //将desc字段变成普通字段  33
//    private String desc;

    //online属性在表中没有,但是我们有需要这个属性进行展示,所以使用TableField注解,  35
    // 去掉这个字段,不让他作为查询字段。
//    @TableField(exist = false)
//    private Integer online; //用户在线或不在线
}

PrimaryKeyTest

//  77
    @Test
    void primary(){
        User user = new User();
        user.setName("Mary");
        user.setAge(35);
        user.setEmail("mary@powernode.com");
        userMapper.insert(user);
    }

拼接的SQL语句如下

主键和分页和ActiveRecord和SimpleQuery_User_05

主键和分页和ActiveRecord和SimpleQuery_主键_06

1.3 INPUT策略   78

该策略表示,必须由我们手动的插入id,否则无法添加数据

主键和分页和ActiveRecord和SimpleQuery_主键_07

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.NONE)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

由于我们不使用AUTO了,所以把自动递增去掉

主键和分页和ActiveRecord和SimpleQuery_主键_08

这里如果我们省略不写id,会发现,无法插入数据

PrimaryKeyTest

@Test
    void primary2(){
        User user = new User();
        user.setName("Mary");
        user.setAge(35);
        user.setEmail("mary@powernode.com");
        userMapper.insert(user);
    }

主键和分页和ActiveRecord和SimpleQuery_主键_09

但是我们自己指定了id,发现可以添加成功

PrimaryKeyTest

//78
    @Test
    void primary2(){
        User user = new User();
        user.setId(8L);
        user.setName("Mary");
        user.setAge(35);
        user.setEmail("mary@powernode.com");
        userMapper.insert(user);
    }

主键和分页和ActiveRecord和SimpleQuery_分页_10

主键和分页和ActiveRecord和SimpleQuery_分页_11

1.4 ASSIGN_ID策略  79

我们来思考一下,像之前这种自动递增的方式,有什么问题?

如果我们将来一张表的数据量很大,我们需要进行分表。

常见的分表策略有两种:

【1】水平拆分   79

主键和分页和ActiveRecord和SimpleQuery_User_12

水平拆分就是将一个大的表按照数据量进行拆分

【2】垂直拆分  79

主键和分页和ActiveRecord和SimpleQuery_主键_13

垂直拆分就是将一个大的表按照字段进行拆分

其实我们对于拆分后的数据,有三点需求,就拿水平拆分来说:

【1】之前的表的主键是有序的,拆分后还是有序的

【2】虽然做了表的拆分,但是每条数据还需要保证主键的唯一性

【3】主键最好不要直接暴露数据的数量,这样容易被外界知道关键信息

那就需要有一种算法,能够实现这三个需求,这个算法就是雪花算法

雪花算法  80

雪花算法是由一个64位的二进制组成的,最终就是一个Long类型的数值。

主要分为四部分存储

【1】1位的符号位,固定值为0

【2】41位的时间戳

【3】10位的机器码,包含5位机器id和5位服务id

【4】12位的序列号

主键和分页和ActiveRecord和SimpleQuery_User_14

使用雪花算法可以实现有序、唯一、且不直接暴露排序的数字。

测试雪花算法  81

主键和分页和ActiveRecord和SimpleQuery_分页_15

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

PrimaryKeyTest

// 测试雪花算法  81
    @Test
    void primary3(){
        User user = new User();
        user.setName("Mary");
        user.setAge(35);
        user.setEmail("mary@powernode.com");
        userMapper.insert(user);
    }

主键和分页和ActiveRecord和SimpleQuery_分页_16

主键和分页和ActiveRecord和SimpleQuery_主键_17

我们可以在插入后发现一个19位长度的id,该id就是雪花算法生成的id,这是二级制的十进制表示形式

1.5  NONE策略   82

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.NONE)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

NONE策略表示不指定主键生成策略,当我们没有指定主键生成策略或者主键策略为NONE的时候,他跟随的是全局策略,那我们来看一下他的全局策略默认是什么

全局配置中 id-type是用于配置主键生成策略的,我们可以看一下id-type的默认值

主键和分页和ActiveRecord和SimpleQuery_分页_18

主键和分页和ActiveRecord和SimpleQuery_User_19

通过查看源码发现,id-type的默认值就是雪花算法

PrimaryKeyTest

// 测试全局默认策略是不是雪花算法  82
    @Test
    void primary4(){
        User user = new User();
        user.setName("Mary");
        user.setAge(35);
        user.setEmail("mary@powernode.com");
        userMapper.insert(user);
    }

主键和分页和ActiveRecord和SimpleQuery_User_20

主键和分页和ActiveRecord和SimpleQuery_User_21

1.6 ASSIGN_UUID策略   83

UUID(Universally Unique Identifier)全局唯一标识符,定义为一个字符串主键,采用32位数字组成,编码采用16进制,定义了在时间和空间都完全唯一的系统信息。

UUID的编码规则:

【1】1~8位采用系统时间,在系统时间上精确到毫秒级保证时间上的唯一性;

【2】9~16位采用底层的IP地址,在服务器集群中的唯一性;

【3】17~24位采用当前对象的HashCode值,在一个内部对象上的唯一性;

【4】25~32位采用调用方法的一个随机数,在一个对象内的毫秒级的唯一性。

通过以上4种策略可以保证唯一性。在系统中需要用到随机数的地方都可以考虑采用UUID算法。

我们想要演示UUID的效果,需要改变一下表的字段类型和实体类的属性类型

将数据库表的字段类型改为varchar(50)

主键和分页和ActiveRecord和SimpleQuery_User_22

将实体类的属性类型改为String,并指定主键生成策略为IdType.ASSIGN_UUID

主键和分页和ActiveRecord和SimpleQuery_分页_23

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    private Integer age;
    private String email;
}

完成数据的添加

PrimaryKeyTest

//测试UUID  83
    @Test
    void primary5(){
        User user = new User();
        user.setName("Mary");
        user.setAge(35);
        user.setEmail("mary@powernode.com");
        userMapper.insert(user);
    }

我们会发现,成功添加了一条数据,id为uuid类型

主键和分页和ActiveRecord和SimpleQuery_主键_24

主键和分页和ActiveRecord和SimpleQuery_User_25

1.7 小结  83

本章节讲解了主键生成策略,我们可以通过指定主键生成策略来生成不同的主键id,从而达到对于数据进行唯一标识的作用。

2. 分页  84

分页操作在实际开发中非常的常见,我们在各种平台和网站中都可以看到分页的效果。

例如:京东商城的分页效果

主键和分页和ActiveRecord和SimpleQuery_分页_26

在MybatisPlus中我们如何配置分页呢?这里我们思考一下

在MybatisPlus中的查询语句是怎么实现的,我们可以通过两种方式实现查询语句

【1】通过MybatisPlus提供的方法来实现条件查询

【2】通过自定义SQL语句的方式来实现查询

接下来我们就来演示这两种分页方式如何实现

2.1  分页插件   84

在大部分场景下,如果我们的SQL没有这么复杂,是可以直接通过MybatisPlus提供的方法来实现查询的,在这种情况下,我们可以通过配置分页插件来实现分页效果

分页的本质就是需要设置一个拦截器,通过拦截器拦截了SQL,通过在SQL语句的结尾添加limit关键字,来实现分页的效果

主键和分页和ActiveRecord和SimpleQuery_主键_27

接下来看一下配置的步骤

【1】通过配置类来指定一个具体数据库的分页插件,因为不同的数据库的方言不同,具体生成的分页语句也会不同,这里我们指定数据库为Mysql数据库

MybatisPlusConfig

package com.powernode.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//配置类   85
//这里我们配置分页插件   85
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

【2】实现分页查询效果

PageTest

package com.powernode;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.powernode.domain.User;
import com.powernode.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

//测试分页效果  85
@SpringBootTest
public class PageTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    void selectPage(){
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //指定分页对象,分页对象包含分页信息 IPage
        IPage<User> userPage = new Page<>(1, 3);//参数1 当前页,参数2一页显示几条
        //执行查询
        userMapper.selectPage(userPage,lambdaQueryWrapper);
        //获取分页查询的信息
        System.out.println("当前页:" + userPage.getCurrent());
        System.out.println("每页显示条数:" + userPage.getSize());
        System.out.println("总页数:" + userPage.getPages());
        System.out.println("总条数:" + userPage.getTotal());
        System.out.println("分页数据:" + userPage.getRecords());
    }
}

主键和分页和ActiveRecord和SimpleQuery_User_28

2.2 自定义分页插件   86

在某些场景下,我们需要自定义SQL语句来进行查询。接下来我们来演示一下自定义SQL的分页操作

【1】在UserMapper.xml映射配置文件中提供查询语句

UserMapper.xml

<!--自定义分页查询 86-->
    <select id="selectByName" resultType="com.powernode.domain.User">
        select * from powershop_user where name = #{name}
    </select>

【2】在Mapper接口中提供对应的方法,方法中将IPage对象作为参数传入

UserMapper

//自定义分页查询  86
    IPage<User> selectByName(IPage<User> page,String name);

【3】表数据为

主键和分页和ActiveRecord和SimpleQuery_User_29

【4】实现分页查询效果

PageTest

//自定义分页查询   86
    @Test
    void selectPage2(){
        IPage<User> userPage = new Page<>(1, 2);
        userMapper.selectByName(userPage,"Mary");

        System.out.println("当前页:" + userPage.getCurrent());
        System.out.println("每页显示条数:" + userPage.getSize());
        System.out.println("总页数:" + userPage.getPages());
        System.out.println("总条数:" + userPage.getTotal());
        System.out.println("分页数据:" + userPage.getRecords());
    }

主键和分页和ActiveRecord和SimpleQuery_User_30

2.3 小结   86

这里我们学习了两种分页的配置方法,将来以后我们在进行条件查询的时候,可以使用分页的配置进行配置。

3. ActiveRecord模式    87

3.1  ActiveRecord介绍

ActiveRecord(活动记录,简称AR),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。ActiveRecord 一直广受解释型动态语言( PHP 、 Ruby 等)的喜爱,通过围绕一个数据对象进行CRUD操作。而 Java 作为准静态(编译型)语言,对于 ActiveRecord 往往只能感叹其优雅,所以 MP 也在 AR 道路上进行了一定的探索,仅仅需要让实体类继承 Model 类且实现主键指定方法,即可开启 AR 之旅。

3.2  ActiveRecord实现   87

接下来我们来看一下ActiveRecord的实现步骤

【1】让实体类继承Model<User>类

主键和分页和ActiveRecord和SimpleQuery_User_31

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User extends Model<User> {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

我们可以看到,Model类中提供了一些增删改查方法,这样的话我们就可以直接使用实体类对象调用这些增删改查方法了,简化了操作的语法,但是他的底层依然是需要UserMapper的,所以持久层接口并不能省略

【2】测试ActiveRecord模式的增删改查

添加数据

ActiveRecordTest

//添加数据  87
    @Test
    void activeRecordAdd(){
        User user = new User();
        user.setName("zhang");
        user.setAge(35);
        user.setEmail("zhang@powernode.com");

        user.insert();
    }

主键和分页和ActiveRecord和SimpleQuery_分页_32

主键和分页和ActiveRecord和SimpleQuery_User_33

删除数据

ActiveRecordTest

//删除操作   87
    @Test
    void activeRecordDelete(){
        User user = new User();
        user.setId(1682300098762629121L);
        user.deleteById();
    }

主键和分页和ActiveRecord和SimpleQuery_分页_34

修改数据

ActiveRecordTest

//修改操作   87
    @Test
    void activeRecordUpdate(){
        User user = new User();
        user.setId(7L);
        user.setAge(50);
        user.updateById();
    }

主键和分页和ActiveRecord和SimpleQuery_User_35

主键和分页和ActiveRecord和SimpleQuery_分页_36

查询数据

ActiveRecordTest

//查询操作   87
    @Test
    void activeRecordSelect(){
        User user = new User();
        user.setId(7L);
        User result = user.selectById();
        System.out.println(result);
    }

主键和分页和ActiveRecord和SimpleQuery_User_37

4. SimpleQuery工具类  88

4.1  SimpleQuery介绍   88

SimpleQuery可以对selectList查询后的结果用Stream流进行了一些封装,使其可以返回一些指定结果,简洁了api的调用

4.2 list

演示基于字段封装集合

ActiveRecordTest

//SimpleQuery工具类 对selectList查询后的结果用Stream流进行了一些封装  88
    @Test
    void testList(){
        List<Long> list = SimpleQuery
                .list(new LambdaQueryWrapper<User>().eq(User::getName, "Mary")
                        , User::getId);
        System.out.println(list);
    }

主键和分页和ActiveRecord和SimpleQuery_分页_38

演示对于封装后的字段进行lambda操作   89

ActiveRecordTest

// 对于封装后的字段进行lambda操作  89
    @Test
    void testList2(){
        List<String> list = SimpleQuery
                .list(new LambdaQueryWrapper<User>().eq(User::getName, "Mary")
                        , User::getName
                        , user -> Optional.of(user.getName())//得到字段
                                .map(String::toLowerCase)//转为小写
                                .ifPresent(user::setName));//将转为小写后的字段重新设置进list
        System.out.println(list);
    }

主键和分页和ActiveRecord和SimpleQuery_分页_39

4.3 map   90

演示将所有的对象以id,实体的方式封装为Map集合

//SimpleQuery工具类 的map操作  90
    @Test
    void testMap(){
        Map<Long, User> map = SimpleQuery
                .keyMap(new LambdaQueryWrapper<User>(), User::getId);
        System.out.println(map);
    }

主键和分页和ActiveRecord和SimpleQuery_主键_40

演示将单个对象以id,实体的方式封装为Map集合   91

//演示将单个对象以id,实体的方式封装为Map集合   91
    @Test
    void testMap2(){
        Map<Long, User> map = SimpleQuery
                .keyMap(new LambdaQueryWrapper<User>().eq(User::getId, 1L)
                        , User::getId);
        System.out.println(map);
    }

主键和分页和ActiveRecord和SimpleQuery_主键_41

演示只想要id和name组成的map   92

//演示只想要id和name组成的map   92
    @Test
    void testMap3(){
        Map<Long, String> map = SimpleQuery
                .map(new LambdaQueryWrapper<User>(), User::getId, User::getName);
        System.out.println(map);
    }

主键和分页和ActiveRecord和SimpleQuery_主键_42

4.4 Group   93

演示分组效果

//SimpleQuery工具类 的group操作  90
    @Test
    void testGroup(){
        Map<String, List<User>> map = SimpleQuery
                .group(new LambdaQueryWrapper<User>(), User::getName);
        System.out.println(map);
    }

主键和分页和ActiveRecord和SimpleQuery_User_43

4.5 小结   93

在这一小节,我们演示了SimpleQuery提供的Stream流式操作的方法,通过这些操作继续为我们提供了查询方面简化的功能

好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695