自定义Mybatis-Plus分布式ID生成器(解决ID长度超过JavaScript整数安全范围问题)
自定义MyBatis-Plus分布式ID生成器(解决ID长度超过JavaScript整数安全范围问题)
版本
MyBatis-Plus 3.4.1
问题
MyBatis-Plus 默认生成的是 64bit 长整型,而 JS 的 Number 类型精度最高只有 53bit,如果以 Long 类型 ID 和前端 JS 进行交互,会出现精度丢失(最后两位数字变成 00) 而导致最终系统报错。
解决方案
一种方案是在响应前端时,将 ID 转换成 String 类型返回,但这个方法治标不治本,因此最终通过采用截短 ID 长度,以避免 ID 超过 JS 整数安全范围。
缩短雪花算法后空间划分(可根据实际需求调整):
1. 高位 32bit 作为秒级时间戳, 时间戳减去固定值(2024 年时间戳)
2. 5bit 作为机器标识, 最高可部署 32 台机器
3. 最后 16bit 作为自增序列, 单节点最高每秒 2^16 = 65536 个 ID
代码实现
通过实现 MyBatis-Plus IdentifierGenerator 接口以自定义 ID 生成器
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** 符合 JavaScript 整数安全范围的自定义ID生成器** @author PANDA*/
@Slf4j
@Component
public class JsSafeIdGenerator implements IdentifierGenerator {/** 初始偏移时间戳 2024-01-01 */private static final long OFFSET = 1704067200L;/** 机器id (0~15 保留 16~31作为备份机器) */private static final long WORKER_ID;/** 机器id所占位数 (5bit, 支持最大机器数 2^5 = 32)*/private static final long WORKER_ID_BITS = 5L;/** 自增序列所占位数 (16bit, 支持最大每秒生成 2^16 = 65536) */private static final long SEQUENCE_ID_BITS = 16L;/** 机器id偏移位数 */private static final long WORKER_SHIFT_BITS = SEQUENCE_ID_BITS;/** 自增序列偏移位数 */private static final long OFFSET_SHIFT_BITS = SEQUENCE_ID_BITS + WORKER_ID_BITS;/** 机器标识最大值 (2^5 / 2 - 1 = 15) */private static final long WORKER_ID_MAX = ((1 << WORKER_ID_BITS) - 1) >> 1;/** 备份机器ID开始位置 (2^5 / 2 = 16) */private static final long BACK_WORKER_ID_BEGIN = (1 << WORKER_ID_BITS) >> 1;/** 自增序列最大值 (2^16 - 1 = 65535) */private static final long SEQUENCE_MAX = (1 << SEQUENCE_ID_BITS) - 1;/** 发生时间回拨时容忍的最大回拨时间 (秒) */private static final long BACK_TIME_MAX = 1L;/** 上次生成ID的时间戳 (秒) */private static long lastTimestamp = 0L;/** 当前秒内序列 (2^16)*/private static long sequence = 0L;/** 备份机器上次生成ID的时间戳 (秒) */private static long lastTimestampBak = 0L;/** 备份机器当前秒内序列 (2^16)*/private static long sequenceBak = 0L;static {// 初始化机器ID 可配置文件获取long workerId = 1;if (workerId < 0 || workerId > WORKER_ID_MAX) {throw new IllegalArgumentException(String.format("worker-id [%d] 越界, 有效范围: 0 ~ %d ", workerId, WORKER_ID_MAX));}WORKER_ID = workerId;}@Overridepublic synchronized Number nextId(Object entity) {return nextId(SystemClock.now() / 1000);}/*** 主机器自增序列* @param timestamp 当前Unix时间戳* @return long*/private static synchronized long nextId(long timestamp) {if (timestamp < lastTimestamp) {log.warn("时钟回拨, 启用备份机器ID: now: [{}] last: [{}]", timestamp, lastTimestamp);return nextIdBackup(timestamp);}if (timestamp != lastTimestamp) {lastTimestamp = timestamp;sequence = 0L;}if (0L == (++sequence & SEQUENCE_MAX)) {sequence--;return nextIdBackup(Math.max(timestamp, lastTimestampBak));}return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS) | (WORKER_ID << WORKER_SHIFT_BITS) | sequence;}/*** 备份机器自增序列* @param timestamp 当前Unix时间戳* @return long*/private static long nextIdBackup(long timestamp) {if (timestamp < lastTimestampBak) {if (lastTimestampBak - (SystemClock.now() / 1000) <= BACK_TIME_MAX) {timestamp = lastTimestampBak;} else {throw new RuntimeException(String.format("时钟回拨: now: [%d] last: [%d]", timestamp, lastTimestampBak));}}if (timestamp != lastTimestampBak) {lastTimestampBak = timestamp;sequenceBak = 0L;}if (0L == (++sequenceBak & SEQUENCE_MAX)) {return nextIdBackup(timestamp + 1);}return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS) | ((WORKER_ID ^ BACK_WORKER_ID_BEGIN) << WORKER_SHIFT_BITS) | sequenceBak;}}
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;/*** 缓存时间戳解决System.currentTimeMillis()高并发下性能问题** @author PANDA**/
public class SystemClock {private final long period;private final AtomicLong now;private SystemClock(long period) {this.period = period;this.now = new AtomicLong(System.currentTimeMillis());scheduleClockUpdating();}/*** 尝试下枚举单例法*/private enum SystemClockEnum {SYSTEM_CLOCK;private SystemClock systemClock;SystemClockEnum() {systemClock = new SystemClock(1);}public SystemClock getInstance() {return systemClock;}}/*** 获取单例对象* @return com.cmallshop.module.core.commons.util.sequence.SystemClock*/private static SystemClock getInstance() {return SystemClockEnum.SYSTEM_CLOCK.getInstance();}/*** 获取当前毫秒时间戳* @return long*/public static long now() {return getInstance().now.get();}/*** 起一个线程定时刷新时间戳*/private void scheduleClockUpdating() {ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1, runnable -> {Thread thread = new Thread(runnable, "System Clock");thread.setDaemon(true);return thread;});scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);}}
SpringBoot 项目中如何引用?
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@RequiredArgsConstructor
public class MybatisPlusConfiguration {@Beanpublic GlobalConfig globalConfig() {GlobalConfig globalConfig = new GlobalConfig();globalConfig.setIdentifierGenerator(new JsSafeIdGenerator());return globalConfig;}}
ID 映射字段添加 @TableId(type = IdType.ASSIGN_ID) 注解
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Base implements Serializable {@TableId(type = IdType.ASSIGN_ID)private Long id;}
相关文章:
自定义Mybatis-Plus分布式ID生成器(解决ID长度超过JavaScript整数安全范围问题)
自定义MyBatis-Plus分布式ID生成器(解决ID长度超过JavaScript整数安全范围问题) 版本 MyBatis-Plus 3.4.1 问题 MyBatis-Plus 默认生成的是 64bit 长整型,而 JS 的 Number 类型精度最高只有 53bit,如果以 Long 类型 ID 和前端…...
2024剪辑神器盘点:四大热门剪辑软件推荐!
亲爱的朋友们,想要制作出精彩短视频,却苦于找不到合适的剪辑工具?别担心,今天要向大家推荐几款剪辑软件,它们能帮助大家更好地完成视频创作! 福昕视频剪辑 链接:www.pdf365.cn/foxit-clip/ 对…...
sql注入靶场sqli-labs常见sql注入漏洞详解
目录 sqli-labs-less1 1.less1普通解法 1.在url里面填写不同的值,返回的内容也不同,证明,数值是进入数据库进行比对了的(可以被注入) 2.判断最终执行的sql语句的后面还有内容吗,并且能够判断是字符型的拼接…...
[C++] 模板进阶:特化与编译链接全解析
文章目录 非类型模板类型形参非类型模板参数代码示例 **模板的特化**为什么要有模板的特化函数模板特化使用场景与示例函数模板特化的实现细节 类模板特化全特化示例 偏特化部分优化通过进一步限制模板参数进行特化偏特化为指针类型示例:偏特化为引用类型示例&#…...
oracle-备份
1、逻辑备份(exp) /ljbb/oracle/o19c/bin/exp hr/hr tablesJOBS file/ljbb/bak/system.sql log/ljbb/bak/ststem.log query\where deptno30\ buffer100000000 hr/hr 用户/密码 tablesJOBS 表名:JOBS file/ljbb/bak/system.sql 备份文件路径 log/ljbb/ba…...
oracle 并行parallel的插入insert用法
在Oracle数据库中,INSERT 语句确实可以使用 Parallel(并行)功能。通过并行插入,可以在插入数据时同时利用多个并行操作进程来执行插入操作,从而显著提高插入操作的速度和效率。这对于需要处理大量数据插入的场景尤为有…...
夜莺监控使用指南
夜莺监控使用指南 本文用于解决在部署和应用夜莺监控中遇到的一些问题以及官方文档缺失的某些步骤可能会遇到的坑。 安装过程 我使用是NightingaleCategrafPrometheus的架构。 Nightingale安装文档:https://flashcat.cloud/docs/content/flashcat-monitor/night…...
MySQLDM笔记-查询库中是否存在列出的表名及查询库中列出的不存在的表名
如下表名: aaa,bb,cc,ccs,dds,csdf,csdfs,sdfa,werwe,csdfsd 在MySQL库中,查询哪些表名在数据库中 SELECT table_name FROM information_schema.tables WHERE table_schema your_database_name_here AND table_name IN (aaa, bb, cc, ccs, dds, csdf…...
第9天 xxl-job
使用xxl-job需要建表 引入依赖 添加配置 Bean public XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> xxl-job config init.");XxlJobSpringExecutor xxlJobSpringExecutor new XxlJobSpringExecutor();xxlJo…...
C++字符串<string>库
一:string及其标准库 C中使用string类需要添加<string>库。 string初始化: string str1 "Hello"; string str2; str2 "World"; string str3 str1 str2; string在变量的声明以及初始化与C语言的char类字符串一致。但是str…...
智能分析,安全无忧:AI视频分析技术在安全生产中的深度应用
在当今快速发展的科技时代,视频智能分析技术(Intelligent Video Analysis,简称IV)已经成为提升安全生产水平的重要手段。这一技术通过计算机图像视觉分析技术,实现了对场景中目标的自动识别和追踪,有效提升…...
02 Canal的安装使用
1 下载Canal Cannal下载地址如下:https://github.com/alibaba/canal/releases,这里选择Canal 1.1.4版本下载。2 上传解压 #首先创建目录 “/software/canal” [rootnode3 ~]# mkdir -p /software/canal#将Canal安装包解压到创建的canal目录中 [rootnode3 ~]# tar …...
【网络安全】玲珑安全第四期
鉴于玲珑安全漏洞挖掘前三期课程取得的优异成绩和获得的强烈反响,我们决定启动玲珑安全第四期漏洞挖掘培训计划。 文章目录 往期学员收获基础学员报喜(部分)课程反馈第四期课程课程内容免费课程往期学员收获 第一期课程总结及学员收获:->点我查看第一期学员收获<- …...
【工具】图片背景移除界面 UI 源码
移除图片背景的UI 照片背景移除和填充颜色的用户界面 仓库地址:https://github.com/MengWoods/remove-background-ui/tree/main 介绍 该项目提供了一个基于 removebg 库的用户界面,用于从输入的照片中移除背景,并用不同的颜色填充背景。 …...
CentOS linux 安装openssl(openssl拒绝服务漏洞【CVE-2022-0778】解决)
一、安装 1.下载相关openssl包 下载地址: https://www.openssl.org/source/ 2.将下载好的压缩包放到 /app/server/nginx 路径下(根据自己实际需求定义) 3.切换至该路径 cd /app/server/nginx4.压缩包解压 压缩包解压 :tar -…...
假如有一个嵌套集合,怎么通过stream流将集合放到一个集合之中?
假如有一个嵌套集合,怎么通过stream流将集合放到一个集合之中? 问题解释:你有一个嵌套的集合,想要通过 Stream 流的方式将其中嵌套的集合放到一个新的集合中。可以使用 flatMap 方法来实现。这种方法非常适合处理嵌套集合的情况。…...
flutter doctor出现 Unable to find bundled Java version
在安装flutter时执行flutter doctor时出现了如下错误: [!] Android Studio (version 2022.1) ✗ Unable to find bundled Java version. 解决办法 检查下Applications/Android Studio.app/Contents目录下有没有jre文件夹,如果没有则创建一个&…...
Linux系统修改root密码
疑难杂症篇(十六)--虚拟机出现“The system is running in low-graphics mode“问题的解决方案_the system is running in low graphic-CSDN博客...
AI时代,我们还可以做什么?
最近看了本书,书名叫做《拐点:站在 AI 颠覆世界的前夜》,作者是万维钢。 本想着看完后,就能掌握一整套 AI 技巧,结果——竟然学了很多道理。 这本书讨论了以下话题: 我们该怎么理解这个 AI 大时代的哲学&am…...
【生成式人工智能-十-文字、图片、声音生成策略】
人工智能生成文字、图片、声音的方法 生成的本质生成的策略文字AR (Autoregressive Generation)图像和视频 NAR(Non-Autoregressive Generation)解决NAR生成品质低的问题 AR NAR 生成的本质 文字、图像、声音的生成的本质,就是给模型一个输入,模型把基…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
