赞
踩
原文网址:MyBatis-Plus--使用雪花算法生成主键ID--使用/分析_IT利刃出鞘的博客-CSDN博客
说明
本文介绍MyBatis-Plus如何使用其自带的雪花算法生成主键ID。
MyBatis-Plus自带的雪花算法
- DROP DATABASE IF EXISTS mp;
- CREATE DATABASE mp DEFAULT CHARACTER SET utf8;
- USE mp;
-
- DROP TABLE IF EXISTS `t_user`;
- SET NAMES utf8mb4;
-
- CREATE TABLE `t_user`
- (
- `id` BIGINT(0) NOT NULL,
- `user_name` VARCHAR(64) NOT NULL COMMENT '用户名(不能重复)',
- `nick_name` VARCHAR(64) NULL COMMENT '昵称(可以重复)',
- `email` VARCHAR(64) COMMENT '邮箱',
- `create_time` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
- `update_time` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
- `deleted_flag` BIGINT(0) NOT NULL DEFAULT 0 COMMENT '0:未删除 其他:已删除',
- PRIMARY KEY (`id`) USING BTREE,
- UNIQUE KEY `index_user_name_deleted_flag` (`user_name`, `deleted_flag`),
- KEY `index_create_time`(`create_time`)
- ) ENGINE = InnoDB COMMENT = '用户';

- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>3.5.1</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>2.3.12.RELEASE</version>
- <relativePath/> <!-- lookup parent from repository -->
- </parent>
- <groupId>com.example</groupId>
- <artifactId>MyBatis-Plus_Single</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>MyBatis-Plus_Single</name>
- <description>Demo project for Spring Boot</description>
-
- <properties>
- <java.version>1.8</java.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
-
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>3.5.1</version>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
-
- <dependency>
- <groupId>com.github.xiaoymin</groupId>
- <artifactId>knife4j-spring-boot-starter</artifactId>
- <version>3.0.3</version>
- </dependency>
-
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-
- </project>

application.yml
- spring:
- datasource:
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://127.0.0.1:3306/mp?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
- username: root
- password: 222333
-
- #mybatis-plus配置控制台打印完整带参数SQL语句
- mybatis-plus:
- configuration:
- log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
MyBatis-Plus配置类
启动类上加注解:
@MapperScan("com.example.demo.**.mapper")
Entity
- package com.example.demo.user.entity;
-
- import com.baomidou.mybatisplus.annotation.IdType;
- import com.baomidou.mybatisplus.annotation.TableId;
- import com.baomidou.mybatisplus.annotation.TableLogic;
- import com.baomidou.mybatisplus.annotation.TableName;
- import lombok.Data;
-
- import java.time.LocalDateTime;
-
- @Data
- @TableName(value = "t_user")
- public class User {
- @TableId(value = "id", type = IdType.ASSIGN_ID)
- // 等价于:
- // @TableId(value = "id", type = IdType.AUTO)
- private Long id;
-
- /**
- * 用户名(不能重复)
- */
- private String userName;
-
- /**
- * 昵称(可以重复)
- */
- private String nickName;
-
- /**
- * 邮箱
- */
- private String email;
-
- /**
- * 创建时间
- */
- private LocalDateTime createTime;
-
- /**
- * 修改时间
- */
- private LocalDateTime updateTime;
-
- /**
- * 0:未删除 其他:已删除
- */
- @TableLogic(delval = "id")
- private Long deletedFlag;
- }

Mapper
- package com.example.demo.user.mapper;
-
- import com.baomidou.mybatisplus.core.conditions.Wrapper;
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import com.example.demo.user.entity.User;
- import org.springframework.stereotype.Repository;
-
- @Repository
- public interface UserMapper extends BaseMapper<User> {
- }
Service
接口
- package com.example.demo.user.service;
-
- import com.baomidou.mybatisplus.extension.service.IService;
- import com.example.demo.user.entity.User;
-
- public interface UserService extends IService<User> {
- }
实现
- package com.example.demo.user.service.impl;
-
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.example.demo.user.entity.User;
- import com.example.demo.user.mapper.UserMapper;
- import com.example.demo.user.service.UserService;
- import org.springframework.stereotype.Service;
-
- @Service
- public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
- }
Controller
- package com.example.demo.user.controller;
-
- import com.example.demo.user.entity.User;
- import com.example.demo.user.service.UserService;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.util.ArrayList;
- import java.util.List;
-
- @Api(tags = "增删改查")
- @RestController
- @RequestMapping("/crud")
- public class CRUDController {
- @Autowired
- private UserService userService;
-
- // 其他代码
-
- @ApiOperation("批量添加")
- @PostMapping("/addBatch")
- public List<User> addBatch() {
- List<User> users = new ArrayList<>();
- for (long i = 0; i < 10; i++) {
- User user = new User();
- user.setUserName("Iron Man" + i);
-
- users.add(user);
- }
- userService.saveBatch(users);
- return users;
- }
- }

访问:http://localhost:8080/doc.html
结果
访问如下位置:
可以发现,生成的ID是19位的数,而且是增加的。这就是雪花算法的生成的ID的现象。
MyBatis-Plus的ID生成算法的配置方式如下:
application.yml:
- mybatis-plus:
- global-config:
- db-config:
- #id类型。
- id-type: ASSIGN_ID # 默认为ASSIGN_ID
id-type默认是: ASSIGN_ID,代码如下:
MyBatis的MybatisParameterHandler#populateKeys方法里对ID进行填充,里边调用到IdentifierGenerator#nextId获取ID:
IdentifierGenerator是一个接口,其默认实现类是:DefaultIdentifierGenerator,其调用Sequence的nextId()获取ID:
点进去看看这个Sequence:
可以发现,这就是雪花算法!
结论
MyBatis-Plus生成机器序号的方法是:前5位作为dataCenterId(通过MAC地址生成),后5位作为workerId(通过MAC地址结合JVM的PID生成)。
分析
DefaultIdentifierGenerator有个构造函数,若没有配置workerId和dataCenterId,则会调用无参构造方法。
上边会调用到Sequence的构造方法:Sequence#Sequence(java.net.InetAddress),如下图所示
追踪dataCenterId和workerId的方法:
- package com.baomidou.mybatisplus.core.toolkit;
-
- import org.apache.ibatis.logging.Log;
- import org.apache.ibatis.logging.LogFactory;
-
- import java.lang.management.ManagementFactory;
- import java.net.InetAddress;
- import java.net.NetworkInterface;
- import java.util.concurrent.ThreadLocalRandom;
-
- /**
- * 分布式高效有序 ID 生产黑科技(sequence)
- *
- * <p>优化开源项目:https://gitee.com/yu120/sequence</p>
- *
- * @author hubin
- * @since 2016-08-18
- */
- public class Sequence {
-
- // 其他代码
-
- /**
- * 获取 maxWorkerId
- */
- protected long getMaxWorkerId(long datacenterId, long maxWorkerId) {
- StringBuilder mpid = new StringBuilder();
- mpid.append(datacenterId);
- String name = ManagementFactory.getRuntimeMXBean().getName();
- if (StringUtils.isNotBlank(name)) {
- /*
- * GET jvmPid
- */
- mpid.append(name.split(StringPool.AT)[0]);
- }
- /*
- * MAC + PID 的 hashcode 获取16个低位
- */
- return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
- }
-
- /**
- * 数据标识id部分
- */
- protected long getDatacenterId(long maxDatacenterId) {
- long id = 0L;
- try {
- if (null == this.inetAddress) {
- this.inetAddress = InetAddress.getLocalHost();
- }
- NetworkInterface network = NetworkInterface.getByInetAddress(this.inetAddress);
- if (null == network) {
- id = 1L;
- } else {
- byte[] mac = network.getHardwareAddress();
- if (null != mac) {
- id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;
- id = id % (maxDatacenterId + 1);
- }
- }
- } catch (Exception e) {
- logger.warn(" getDatacenterId: " + e.getMessage());
- }
- return id;
- }
- }

说明
MyBatis-Plus也提供了一个雪花算法工具类,想用的可以使用:com.baomidou.mybatisplus.core.toolkit.IdWorker。
用法
- public static void main(String[] args) {
- // 返回值 1385106677482582018
- System.out.println(IdWorker.getId());
- // 返回值 "1385106677482582019"
- System.out.println(IdWorker.getIdStr());
- }
注意
这只是个工具类,与MyBatis-Plus和它的ID默认的雪花算法无关。
在github中有一个很流行的分布式统一ID生成框架也叫idworker,需要和mybatis-plus中自带的Idworker工具类区分开来。它是一个基于zookeeper和snowflake算法的分布式统一ID生成工具,通过zookeeper自动注册机器(最多1024台),无需手动指定workerId和dataCenterId。idworker官网:https://github.com/imadcn/idworker
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。