展示一個執行緒安全的流水號產生器 SerialNum,格式為 CB + yyyyMMdd + 三位流水碼(如 CB20240520001),支援跨日歸零、重啟後透過資料庫最大值同步避免重複。
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
public class SerialNum {
private static String count = "000"; // 當日流水碼
private static String dateValue = "20131115"; // 最後產生日期
/** 產生下一筆流水號 */
public synchronized static String getMoveOrderNo() {
long nowNum = Long.parseLong(new SimpleDateFormat("yyyyMMdd").format(new Date()));
if (nowNum != Long.parseLong(dateValue)) { // 跨日歸零
count = "000";
dateValue = String.valueOf(nowNum);
}
String serial = "CB" + dateValue + nextCount(count);
return serial;
}
/** 從資料庫最大值同步(重啟/遷移時呼叫) */
public synchronized static String getMoveOrderNo(String lastSerialFromDB) {
if (lastSerialFromDB == null || lastSerialFromDB.length() < 12) return getMoveOrderNo();
String dbDate = StringUtils.substring(lastSerialFromDB, 2, 10); // yyyyMMdd
String dbCount = StringUtils.substring(lastSerialFromDB, 10); // 流水碼
if (Integer.parseInt(dbCount) > Integer.parseInt(count)) {
dateValue = dbDate;
count = dbCount;
}
return getMoveOrderNo();
}
private static String nextCount(String curr) {
int next = Integer.parseInt(curr) + 1;
String rs = String.valueOf(next);
while (rs.length() < 3) rs = "0" + rs; // 左補零至 3 位
count = rs;
return rs;
}
// 測試
public static void main(String[] args) {
for (int i = 0; i < 10; i++) System.out.println(getMoveOrderNo());
}
}
CB20240520001
CB20240520002
...
CB20240520010
| 特性 | 實作方式 | 備註 |
|---|---|---|
| 執行緒安全 | 方法加 synchronized | 單機單 JVM 環境足夠;分叢集需分散式鎖或 DB 序列 |
| 日期歸零 | 比對 yyyyMMdd 整數 | 跨日自動重置 count |
| 持久化同步 | getMoveOrderNo(lastSerial) | 啟動時查詢 DB MAX(serial) 傳入,確保不重複 |
| 格式固定長 | CB + 8 碼日期 + 3 碼流水 | 共 13 碼,利於資料庫索引與排序 |