在SpringBoot中實(shí)現(xiàn)一個訂單號生成系統(tǒng)的示例代碼
1. UUID
最簡單的方法是使用UUID生成唯一的訂單號。UUID(Universally Unique Identifier)是一種廣泛使用的標(biāo)識符,由128位組成,通常以32個十六進(jìn)制數(shù)字表示,分為五組,形式為8-4-4-4-12的字符串,例如123e4567-e89b-12d3-a456-426614174000
。UUID全球唯一,實(shí)現(xiàn)簡單,但缺點(diǎn)是UUID較長,不易記憶和存儲。
實(shí)例代碼
Java中生成UUID的示例代碼如下:
import java.util.UUID; public class UUIDGenerator { public static String generateUUID() { // 生成一個UUID UUID uuid = UUID.randomUUID(); // 將UUID轉(zhuǎn)換為字符串 String uuidAsString = uuid.toString(); // 返回UUID字符串 return uuidAsString; } public static void main(String[] args) { String uuid = generateUUID(); System.out.println("Generated UUID: " + uuid); } }
2. 數(shù)據(jù)庫序列或自增ID
利用數(shù)據(jù)庫的序列(如PostgreSQL的SEQUENCE)或自增ID(如MySQL的AUTO_INCREMENT)生成唯一的訂單號。數(shù)據(jù)庫序列或自增ID是一種常見的生成唯一標(biāo)識符的方法,特別是在單體應(yīng)用或非分布式系統(tǒng)中。這種方法依賴于數(shù)據(jù)庫的內(nèi)置機(jī)制來保證每次插入新記錄時自動產(chǎn)生一個唯一的標(biāo)識符,缺點(diǎn)是難以在分布式環(huán)境中維護(hù)唯一性。
// 假設(shè)使用JPA @Entity public class Order { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; // 其他屬性 }
數(shù)據(jù)庫序列(如PostgreSQL的SEQUENCE)
CREATE SEQUENCE order_id_seq START WITH 1 INCREMENT BY 1; CREATE TABLE orders ( order_id bigint NOT NULL DEFAULT nextval('order_id_seq'), order_data text );
自增ID(如MySQL的AUTO_INCREMENT)
CREATE TABLE orders ( order_id INT AUTO_INCREMENT, order_data TEXT, PRIMARY KEY (order_id) );
3. 時間戳+隨機(jī)數(shù)/序列
結(jié)合時間戳和隨機(jī)數(shù)(或自定義序列)生成訂單號,以保證唯一性和可讀性??梢酝ㄟ^添加業(yè)務(wù)相關(guān)的前綴來增強(qiáng)業(yè)務(wù)相關(guān)性。
實(shí)例代碼
以下是一個簡單的Java示例,展示了如何結(jié)合時間戳、隨機(jī)數(shù)和業(yè)務(wù)前綴生成訂單號:
import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ThreadLocalRandom; public class OrderNumberGenerator { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); private static final int RANDOM_NUM_BOUND = 10000; // 定義隨機(jī)數(shù)范圍 public static String generateOrderNumber(String prefix) { // 生成時間戳部分 String timestamp = dateFormat.format(new Date()); // 生成隨機(jī)數(shù)部分 int randomNumber = ThreadLocalRandom.current().nextInt(RANDOM_NUM_BOUND); // 組合成訂單號 return prefix + timestamp + String.format("%04d", randomNumber); } public static void main(String[] args) { // 示例:生成訂單號,假設(shè)業(yè)務(wù)前綴為"ORD" String orderNumber = generateOrderNumber("ORD"); System.out.println("Generated Order Number: " + orderNumber); } }
4. 分布式唯一ID生成方案
在分布式系統(tǒng)中,可以使用像Twitter的Snowflake算法生成唯一的ID。Snowflake算法可以生成一個64位的長整數(shù),其中包含時間戳、數(shù)據(jù)中心ID、機(jī)器ID和序列號,以確保生成的ID既唯一又有序。
Snowflake ID結(jié)構(gòu)
Snowflake生成的64位ID可以分為以下幾個部分:
- 1位符號位:由于整數(shù)的最高位是符號位,且64位整數(shù)中的最高位為符號位,通常這一位為0,保證ID為正數(shù)。
- 41位時間戳位:記錄時間戳的差值(相對于某個固定的時間點(diǎn)),單位到毫秒。41位時間戳可以使用69年。
- 10位數(shù)據(jù)中心ID和機(jī)器ID:通常分為5位數(shù)據(jù)中心ID和5位機(jī)器ID,最多支持32個數(shù)據(jù)中心,每個數(shù)據(jù)中心最多支持32臺機(jī)器。
- 12位序列號:用來記錄同一毫秒內(nèi)生成的不同ID,12位序列號支持每個節(jié)點(diǎn)每毫秒產(chǎn)生4096個ID序號。
以下是一個簡化的Snowflake算法實(shí)現(xiàn)示例:
public class SnowflakeIdGenerator { private long datacenterId; // 數(shù)據(jù)中心ID private long machineId; // 機(jī)器ID private long sequence = 0L; // 序列號 private long lastTimestamp = -1L; // 上一次時間戳 private final long twepoch = 1288834974657L; private final long datacenterIdBits = 5L; private final long machineIdBits = 5L; private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private final long maxMachineId = -1L ^ (-1L << machineIdBits); private final long sequenceBits = 12L; private final long machineIdShift = sequenceBits; private final long datacenterIdShift = sequenceBits + machineIdBits; private final long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits; private final long sequenceMask = -1L ^ (-1L << sequenceBits); public SnowflakeIdGenerator(long datacenterId, long machineId) { if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException("datacenterId can't be greater than %d or less than 0"); } if (machineId > maxMachineId || machineId < 0) { throw new IllegalArgumentException("machineId can't be greater than %d or less than 0"); } this.datacenterId = datacenterId; this.machineId = machineId; } public synchronized long nextId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (machineId << machineIdShift) | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } }
下面是對這段代碼的逐行解釋:
類定義和變量初始化
private long datacenterId;
定義數(shù)據(jù)中心ID。private long machineId;
定義機(jī)器ID。private long sequence = 0L;
序列號,用于同一毫秒內(nèi)生成多個ID時區(qū)分這些ID。private long lastTimestamp = -1L;
上一次生成ID的時間戳。
以下是Snowflake算法的一些關(guān)鍵參數(shù):
private final long twepoch = 1288834974657L;
系統(tǒng)的起始時間戳,這里是Snowflake算法的作者選擇的一個固定的時間點(diǎn)(2010-11-04 09:42:54.657 GMT)。private final long datacenterIdBits = 5L;
數(shù)據(jù)中心ID所占的位數(shù)。private final long machineIdBits = 5L;
機(jī)器ID所占的位數(shù)。private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
數(shù)據(jù)中心ID的最大值,這里通過位運(yùn)算計(jì)算得出。private final long maxMachineId = -1L ^ (-1L << machineIdBits);
機(jī)器ID的最大值,同樣通過位運(yùn)算得出。private final long sequenceBits = 12L;
序列號占用的位數(shù)。
以下是一些用于位運(yùn)算的參數(shù),用于計(jì)算最終的ID:
private final long machineIdShift = sequenceBits;
機(jī)器ID的偏移位數(shù)。private final long datacenterIdShift = sequenceBits + machineIdBits;
數(shù)據(jù)中心ID的偏移位數(shù)。private final long timestampLeftShift = sequenceBits + machineIdBits + datacenterIdBits;
時間戳的偏移位數(shù)。private final long sequenceMask = -1L ^ (-1L << sequenceBits);
用于保證序列號在指定范圍內(nèi)循環(huán)。
構(gòu)造函數(shù)
- 構(gòu)造函數(shù)
SnowflakeIdGenerator(long datacenterId, long machineId)
接收數(shù)據(jù)中心ID和機(jī)器ID作為參數(shù),并對這些參數(shù)進(jìn)行校驗(yàn),確保它們在合法范圍內(nèi)。
ID生成方法
public synchronized long nextId()
是生成ID的核心方法,使用synchronized
保證線程安全。- 首先獲取當(dāng)前時間戳。
- 如果當(dāng)前時間戳小于上一次生成ID的時間戳,拋出異常,因?yàn)闀r鐘回?fù)軙?dǎo)致ID重復(fù)。
- 如果當(dāng)前時間戳等于上一次的時間戳(即同一毫秒內(nèi)),通過增加序列號生成不同的ID;如果序列號溢出(超過最大值),則等待到下一個毫秒。
- 如果當(dāng)前時間戳大于上一次的時間戳,重置序列號為0。
- 最后,將時間戳、數(shù)據(jù)中心ID、機(jī)器ID和序列號按照各自的偏移量左移,然后進(jìn)行位或運(yùn)算,組合成一個64位的ID。
輔助方法
private long tilNextMillis(long lastTimestamp)
是一個輔助方法,用于在序列號溢出時等待直到下一個毫秒。
以上就是在SpringBoot中設(shè)計(jì)一個訂單號生成系統(tǒng)的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot訂單號生成系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Maven工程引入依賴失敗Dependencies全部飄紅問題
這篇文章主要介紹了Maven工程引入依賴失敗Dependencies全部飄紅問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08java Date裝成英文String后,無法再轉(zhuǎn)回Date的解決方案
本文介紹了java Date裝成英文String后,無法再轉(zhuǎn)回Date的解決方案。具有一定的參考價值,下面跟著小編一起來看下吧2017-01-01SpringBoot中的@Conditional?注解的使用
@Conditional是Spring4新提供的注解,它的作用是按照一定的條件進(jìn)行判斷,滿足條件的才給容器注冊Bean,本文主要介紹了SpringBoot中的@Conditional?注解的使用2024-01-01詳解IDEA下Gradle多模塊(項(xiàng)目)的構(gòu)建
這篇文章主要介紹了詳解IDEA下Gradle多模塊(項(xiàng)目)的構(gòu)建,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01spring boot 本地圖片不能加載(圖片路徑)的問題及解決方法
這篇文章主要介紹了spring boot 本地圖片不能加載(圖片路徑)的問題,解決的辦法其實(shí)很簡單,只要寫一個配置文件,也就是圖片位置的轉(zhuǎn)化器,原理是虛擬一個在服務(wù)器上的文件夾,與本地圖片的位置進(jìn)行匹配。需要的朋友可以參考下2018-04-04SpringBoot+Vue中的Token續(xù)簽機(jī)制
本文主要介紹了SpringBoot+Vue中的Token續(xù)簽機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06