Mybatis Plus 自定义ID生成器
问题描述
在使用Mybatis Plus的ID生成器 ASSIGN_ID 雪花算法ID时,默认长度19位,前端用的number类型接收的,会导致JS精度损失,所以自定义ID生成器,缩短下ID长度。
/**
* 自增ID @TableId(type = IdType. AUTO)
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
解决
自定义ID生成器, 见官网 链接: 自定义ID生成器
1 声明注入的Bean
// 声明为 Bean 供 Spring 扫描注入
@Slf4j
@Component
public class CustomIdGenerator implements IdentifierGenerator {
@Override
public Number nextId(Object entity) {
log.info(SnowFlake.getInstance().toString());
// 返回生成的id值即可
return SnowFlake.generatorId();
}
}
2 ID生成
/**
* @Description - ID生成规则
* 1 41 10 12
* +--+-------------------+----------------+----------------+
* | | 时间戳 | 机器标识 | 序列号 |
* +--+-------------------+----------------+----------------+
* 时间戳:41 位,表示生成 ID 的时间戳,精确到毫秒级。
* 机器标识:10 位,表示机器的唯一标识,通常使用数据中心和机器编号组合而成。
* 序列号:12 位,表示同一毫秒内的序列号,范围从 0 到 4095
* @Version 1.0
*/
public class SnowFlake {
private static final SnowFlake SNOWFLAKE1 = new SnowFlake(1, 1);
public static SnowFlake getInstance() {
return SNOWFLAKE1;
}
public static long generatorId() {
return SNOWFLAKE1.nextId();
}
/**
* 起始的时间戳,这个时间戳可以是你的系统初始时间,一般取当前时间戳
* 2023-01-01 00:00:00 1672502400000L
* 2023-11-11 11:11:11 1699672271000L
*/
private final static long START_TIMESTAMP = 1672502400000L;
/**
* 每一部分占用的位数,可以根据自己的需求进行调整,这里是按照默认的占位数进行分配
* 序列号占用的位数 12
*/
private final static long SEQUENCE_BIT = 6;
/**
* 机器标识占用的位数 5
*/
private final static long MACHINE_BIT = 1;
/**
* 数据中心占用的位数 5
*/
private final static long DATA_CENTER_BIT = 1;
/**
* 每一部分的最大值,可以根据占用的位数进行计算得到
*/
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
private final static long MAX_DATA_CENTER_NUM = ~(-1L << DATA_CENTER_BIT);
/**
* 每一部分向左的位移,计算出来的值是为了后面生成 ID 做准备
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;
/**
* 数据中心 ID
*/
private final long dataCenterId;
/**
* 机器 ID
*/
private final long machineId;
/**
* 序列号
*/
private long sequence = 0L;
/**
* 上一次时间戳
*/
private long lastTimeStamp = -1L;
/**
* 构造方法
*
* @param dataCenterId 数据中心 ID
* @param machineId 机器 ID
*/
public SnowFlake(long dataCenterId, long machineId) {
if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {
throw new IllegalArgumentException("数据中心标识不能大于等于 " + MAX_DATA_CENTER_NUM + " 或小于 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("机器标识不能大于等于 " + MAX_MACHINE_NUM + " 或小于 0");
}
this.dataCenterId = dataCenterId;
this.machineId = machineId;
}
/**
* 雪花算法核心方法
* 通过调用 nextId() 方法,让当前这台机器上的 snowflake 算法程序生成一个全局唯一的 id
*/
public synchronized long nextId() {
// 获取系统当前时间戳
long currentTimeStamp = getSystemCurrentTimeMillis();
if (currentTimeStamp < lastTimeStamp) {
throw new RuntimeException("时钟向后移动,拒绝生成雪花算法ID");
}
if (currentTimeStamp == lastTimeStamp) {
// 当前毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
// 序列号超出范围,需要等待下一毫秒
if (sequence == 0L) {
// 获取下一毫秒
currentTimeStamp = getNextMill(lastTimeStamp);
}
} else {
// 不同毫秒内,序列号置为 0
sequence = 0L;
}
lastTimeStamp = currentTimeStamp;
// 使用位运算生成最终的 ID
return (currentTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT | dataCenterId << DATA_CENTER_LEFT | machineId << MACHINE_LEFT | sequence;
}
/**
* 获取系统当前时间戳
*
* @return 当前时间(毫秒)
*/
private long getSystemCurrentTimeMillis() {
return System.currentTimeMillis();
}
/**
* 获取下一毫秒
* 当某一毫秒的时间,产生的 id 数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生 ID
*
* @param lastTimestamp 上次生成 ID 的时间截
* @return 当前时间戳
*/
private long getNextMill(long lastTimestamp) {
long timeMillis = getSystemCurrentTimeMillis();
while (timeMillis <= lastTimestamp) {
timeMillis = getSystemCurrentTimeMillis();
}
return timeMillis;
}
// public static final SnowFlake SNOWFLAKE1 = new SnowFlake(1, 1);
// public static SnowFlake SNOWFLAKE2 = new SnowFlake(2, 1);
// public static SnowFlake SNOWFLAKE3 = new SnowFlake(3, 1);
/**
* 测试类
*/
public static void main(String[] args) {
int a = 30;
for (int i = 0; i < a; i++) {
System.out.println("数据中心1,雪花算法 ID:" + SNOWFLAKE1.nextId());
System.out.println("数据中心1,雪花算法 ID:" + SNOWFLAKE1.nextId());
System.out.println("数据中心1,雪花算法 ID:" + SNOWFLAKE1.nextId());
// System.out.println("数据中心2,雪花算法 ID:" + SNOWFLAKE2.nextId());
// System.out.println("数据中心3,雪花算法 ID:" + SNOWFLAKE3.nextId());
}
}
}