写点什么

[MyBatisPlus]id 生成策略控制

作者:fake smile by
  • 2022 年 9 月 14 日
    黑龙江
  • 本文字数:5026 字

    阅读完需:约 16 分钟

id 生成策略控制

环境构建

  • 创建一个 SpringBoot 项目

  • 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>2.5.0</version>          <relativePath/> <!-- lookup parent from repository -->      </parent>      <groupId>com.itheima</groupId>      <artifactId>mybatisplus_03_dml</artifactId>      <version>0.0.1-SNAPSHOT</version>      <properties>          <java.version>1.8</java.version>      </properties>      <dependencies>            <dependency>              <groupId>com.baomidou</groupId>              <artifactId>mybatis-plus-boot-starter</artifactId>              <version>3.4.1</version>          </dependency>            <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter</artifactId>          </dependency>            <dependency>              <groupId>com.alibaba</groupId>              <artifactId>druid</artifactId>              <version>1.1.16</version>          </dependency>            <dependency>              <groupId>mysql</groupId>              <artifactId>mysql-connector-java</artifactId>              <scope>runtime</scope>          </dependency>            <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-test</artifactId>              <scope>test</scope>          </dependency>            <dependency>              <groupId>org.projectlombok</groupId>              <artifactId>lombok</artifactId>              <version>1.18.12</version>          </dependency>        </dependencies>        <build>          <plugins>              <plugin>                  <groupId>org.springframework.boot</groupId>                  <artifactId>spring-boot-maven-plugin</artifactId>              </plugin>          </plugins>      </build>    </project>
复制代码


  • 编写 UserDao 接口


  @Mapper  public interface UserDao extends BaseMapper<User> {  }
复制代码


  • 编写模型类


  @Data  @TableName("tbl_user")  public class User {      private Long id;      private String name;      @TableField(value="pwd",select=false)      private String password;      private Integer age;      private String tel;      @TableField(exist=false)      private Integer online;  }
复制代码


  • 编写引导类


  @SpringBootApplication  public class Mybatisplus03DqlApplication {        public static void main(String[] args) {          SpringApplication.run(Mybatisplus03DqlApplication.class, args);      }    }
复制代码


  • 编写配置文件


  # dataSource  spring:    datasource:      type: com.alibaba.druid.pool.DruidDataSource      driver-class-name: com.mysql.cj.jdbc.Driver      url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC      username: root      password: root  # mp日志  mybatis-plus:    configuration:      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
复制代码


  • 编写测试类


  @SpringBootTest  class Mybatisplus02DqlApplicationTests {        @Autowired      private UserDao userDao;            @Test      void testGetAll(){          List<User> userList = userDao.selectList(null);          System.out.println(userList);      }  }
复制代码


  • 测试


  @SpringBootTest  class Mybatisplus03DqlApplicationTests {        @Autowired      private UserDao userDao;          @Test      void testSave(){          User user = new User();          user.setName("黑马程序员");          user.setPassword("itheima");          user.setAge(12);          user.setTel("4006184000");          userDao.insert(user);      }      @Test      void testDelete(){          userDao.deleteById(1401856123925713409L)      }      @Test      void testUpdate(){          User user = new User();          user.setId(3L);          user.setName("Jock666");          user.setVersion(1);          userDao.updateById(user);      }  }
复制代码


  • 最终创建的项目结构为:



前面我们在新增的时候留了一个问题,就是新增成功后,主键 ID 是一个很长串的内容,我们更想要的是按照数据库表字段进行自增长,在解决这个问题之前,我们先来分析下 ID 该如何选择:


  • 不同的表应用不同的 id 生成策略

  • 日志:自增(1,2,3,4,……)

  • 购物订单:特殊规则(FQ23948AK3843)

  • 外卖单:关联地区日期等信息(10 04 20200314 34 91)

  • 关系表:可省略 id

  • ……


不同的业务采用的 ID 生成方式应该是不一样的,那么在 MP 中都提供了哪些主键生成策略,以及我们该如何进行选择?


在这里我们又需要用到 MP 的一个注解叫@TableId

@TableId

AUTO 策略

步骤 1:设置生成策略为 AUTO


@Data@TableName("tbl_user")public class User {    @TableId(type = IdType.AUTO)    private Long id;    private String name;    @TableField(value="pwd",select=false)    private String password;    private Integer age;    private String tel;    @TableField(exist=false)    private Integer online;}
复制代码


步骤 2:删除测试数据并修改自增值


  • 删除测试数据


  • 因为之前生成主键 ID 的值比较长,会把 MySQL 的自动增长的值变的很大,所以需要将其调整为目前最新的 id 值。



步骤 3:运行新增方法


会发现,新增成功,并且主键 id 也是从 5 开始



经过这三步的演示,会发现AUTO的作用是==使用数据库 ID 自增==,在使用该策略的时候一定要确保对应的数据库表设置了 ID 主键自增,否则无效。


接下来,我们可以进入源码查看下 ID 的生成策略有哪些?


打开源码后,你会发现并没有看到中文注释,这就需要我们点击右上角的Download Sources,会自动帮你把这个类的 java 文件下载下来,我们就能看到具体的注释内容。因为这个技术是国人制作的,所以他代码中的注释还是比较容易看懂的。



当把源码下载完后,就可以看到如下内容:



从源码中可以看到,除了 AUTO 这个策略以外,还有如下几种生成策略:


  • NONE: 不设置 id 生成策略

  • INPUT:用户手工输入 id

  • ASSIGN_ID:雪花算法生成 id(可兼容数值型与字符串型)

  • ASSIGN_UUID:以 UUID 生成算法作为 id 生成策略

  • 其他的几个策略均已过时,都将被 ASSIGN_ID 和 ASSIGN_UUID 代替掉。


拓展:


分布式 ID 是什么?


  • 当数据量足够大的时候,一台数据库服务器存储不下,这个时候就需要多台数据库服务器进行存储

  • 比如订单表就有可能被存储在不同的服务器上

  • 如果用数据库表的自增主键,因为在两台服务器上所以会出现冲突

  • 这个时候就需要一个全局唯一 ID,这个 ID 就是分布式 ID。

INPUT 策略

步骤 1:设置生成策略为 INPUT


@Data@TableName("tbl_user")public class User {    @TableId(type = IdType.INPUT)    private Long id;    private String name;    @TableField(value="pwd",select=false)    private String password;    private Integer age;    private String tel;    @TableField(exist=false)    private Integer online;}
复制代码


注意:这种 ID 生成策略,需要将表的自增策略删除掉



步骤 2:添加数据手动设置 ID


@SpringBootTestclass Mybatisplus03DqlApplicationTests {
@Autowired private UserDao userDao; @Test void testSave(){ User user = new User(); //设置主键ID的值 user.setId(666L); user.setName("黑马程序员"); user.setPassword("itheima"); user.setAge(12); user.setTel("4006184000"); userDao.insert(user); }}
复制代码


步骤 3:运行新增方法


如果没有设置主键 ID 的值,则会报错,错误提示就是主键 ID 没有给值:



如果设置了主键 ID,则数据添加成功,如下:


ASSIGN_ID 策略

步骤 1:设置生成策略为 ASSIGN_ID


@Data@TableName("tbl_user")public class User {    @TableId(type = IdType.ASSIGN_ID)    private Long id;    private String name;    @TableField(value="pwd",select=false)    private String password;    private Integer age;    private String tel;    @TableField(exist=false)    private Integer online;}
复制代码


步骤 2:添加数据不设置 ID


@SpringBootTestclass Mybatisplus03DqlApplicationTests {
@Autowired private UserDao userDao; @Test void testSave(){ User user = new User(); user.setName("黑马程序员"); user.setPassword("itheima"); user.setAge(12); user.setTel("4006184000"); userDao.insert(user); }}
复制代码


**注意:**这种生成策略,不需要手动设置 ID,如果手动设置 ID,则会使用自己设置的值。


步骤 3:运行新增方法



生成的 ID 就是一个 Long 类型的数据。

ASSIGN_UUID 策略

步骤 1:设置生成策略为 ASSIGN_UUID


使用 uuid 需要注意的是,主键的类型不能是 Long,而应该改成 String 类型


@Data@TableName("tbl_user")public class User {    @TableId(type = IdType.ASSIGN_UUID)    private String id;    private String name;    @TableField(value="pwd",select=false)    private String password;    private Integer age;    private String tel;    @TableField(exist=false)    private Integer online;}
复制代码


步骤 2:修改表的主键类型



主键类型设置为 varchar,长度要大于 32,因为 UUID 生成的主键为 32 位,如果长度小的话就会导致插入失败。


步骤 3:添加数据不设置 ID


@SpringBootTestclass Mybatisplus03DqlApplicationTests {
@Autowired private UserDao userDao; @Test void testSave(){ User user = new User(); user.setName("黑马程序员"); user.setPassword("itheima"); user.setAge(12); user.setTel("4006184000"); userDao.insert(user); }}
复制代码


步骤 4:运行新增方法


雪花算法

雪花算法(SnowFlake),是 Twitter 官方给出的算法实现 是用 Scala 写的。其生成的结果是一个 64bit 大小整数,它的结构如下图:



  1. 1bit,不用,因为二进制中最高位是符号位,1 表示负数,0 表示正数。生成的 id 一般都是用整数,所以最高位固定为 0。

  2. 41bit-时间戳,用来记录时间戳,毫秒级

  3. 10bit-工作机器 id,用来记录工作机器 id,其中高位 5bit 是数据中心 ID 其取值范围 0-31,低位 5bit 是工作节点 ID 其取值范围 0-31,两个组合起来最多可以容纳 1024 个节点

  4. 序列号占用 12bit,每个节点每毫秒 0 开始不断累加,最多可以累加到 4095,一共可以产生 4096 个 ID

ID 生成策略对比

介绍了这些主键 ID 的生成策略,我们以后该用哪个呢?


  • NONE: 不设置 id 生成策略,MP 不自动生成,约等于 INPUT,所以这两种方式都需要用户手动设置,但是手动设置第一个问题是容易出现相同的 ID 造成主键冲突,为了保证主键不冲突就需要做很多判定,实现起来比较复杂

  • AUTO:数据库 ID 自增,这种策略适合在数据库服务器只有 1 台的情况下使用,不可作为分布式 ID 使用

  • ASSIGN_UUID:可以在分布式的情况下使用,而且能够保证唯一,但是生成的主键是 32 位的字符串,长度过长占用空间而且还不能排序,查询性能也慢

  • ASSIGN_ID:可以在分布式的情况下使用,生成的是 Long 类型的数字,可以排序性能也高,但是生成的策略和服务器时间有关,如果修改了系统时间就有可能导致出现重复主键


综上所述,每一种主键策略都有自己的优缺点,根据自己项目业务的实际情况来选择使用才是最明智的选择。

发布于: 刚刚阅读数: 3
用户头像

fake smile by

关注

还未添加个人签名 2022.07.31 加入

还未添加个人简介

评论

发布
暂无评论
[MyBatisPlus]id生成策略控制_Java_fake smile by_InfoQ写作社区