Designing an Order Number Generation System in Spring Boot
This article presents several approaches for generating unique, scalable order numbers in Spring Boot, including UUIDs, database sequences or auto‑increment IDs, timestamp‑based strings with random components, and distributed Snowflake IDs, each accompanied by Java code examples and discussion of their advantages and drawbacks.
Designing an order number generation system in Spring Boot requires uniqueness, scalability, and possible business relevance. The article outlines four common solutions with Java code examples.
1. UUID
The simplest method uses a UUID, a 128‑bit globally unique identifier represented as a string such as 123e4567‑e89b‑12d3‑a456‑426614174000. UUIDs guarantee uniqueness but are long and not easy to remember.
Example code:
import java.util.UUID;
public class UUIDGenerator {
public static String generateUUID() {
// Generate a UUID
UUID uuid = UUID.randomUUID();
// Convert UUID to string
String uuidAsString = uuid.toString();
// Return UUID string
return uuidAsString;
}
public static void main(String[] args) {
String uuid = generateUUID();
System.out.println("Generated UUID: " + uuid);
}
}2. Database Sequence or Auto‑Increment ID
Using a database‑provided sequence (e.g., PostgreSQL SEQUENCE ) or an auto‑increment column (e.g., MySQL AUTO_INCREMENT ) yields unique identifiers without extra logic. This works well for monolithic or non‑distributed applications but is harder to keep unique across multiple nodes.
JPA entity example:
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// other fields
}PostgreSQL sequence and table creation:
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
);MySQL auto‑increment table:
CREATE TABLE orders (
order_id INT AUTO_INCREMENT,
order_data TEXT,
PRIMARY KEY (order_id)
);3. Timestamp + Random / Sequence
Combining a timestamp with a random number (or a custom sequence) produces readable, unique order numbers and allows a business prefix.
Example implementation:
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; // 0‑9999
public static String generateOrderNumber(String prefix) {
// Timestamp part
String timestamp = dateFormat.format(new Date());
// Random part
int randomNumber = ThreadLocalRandom.current().nextInt(RANDOM_NUM_BOUND);
// Combine
return prefix + timestamp + String.format("%04d", randomNumber);
}
public static void main(String[] args) {
// Example: prefix "ORD"
String orderNumber = generateOrderNumber("ORD");
System.out.println("Generated Order Number: " + orderNumber);
}
}4. Distributed Unique ID – Snowflake
For distributed systems, the Snowflake algorithm (originated by Twitter) generates 64‑bit IDs composed of a sign bit, a 41‑bit timestamp, 5‑bit data‑center ID, 5‑bit machine ID, and a 12‑bit sequence number, ensuring uniqueness and ordering.
Key fields:
1 sign bit (always 0)
41 timestamp bits (milliseconds since a custom epoch)
10 bits for data‑center and machine IDs
12 sequence bits for IDs generated within the same millisecond
Simplified Java implementation:
public class SnowflakeIdGenerator {
private long datacenterId; // data‑center ID
private long machineId; // machine 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 " + maxDatacenterId + " or less than 0");
}
if (machineId > maxMachineId || machineId < 0) {
throw new IllegalArgumentException("machineId can't be greater than " + maxMachineId + " 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;
}
}The code defines the necessary bit allocations, validates input IDs, and provides a thread‑safe nextId() method that assembles the final 64‑bit identifier.
Note: The original source also contains promotional text inviting readers to join a backend‑focused technical group; this part is unrelated to the technical content.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.