自定义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 生成的本质 文字、图像、声音的生成的本质,就是给模型一个输入,模型把基…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
