分散式系統常需要全域唯一、有序的序號(如訂單號、流水號)。本文分析 SequenceDaoJdbcImpl,展示如何利用 Spring JdbcTemplate 衍生類實現線程安全的序號分配。
CREATE TABLE sequence (
seq_name VARCHAR(50) PRIMARY KEY,
seq_value INT NOT NULL
);
SELECT_SQL = "SELECT seq_name, seq_value FROM sequence WHERE seq_name = ?"
UPDATE_SQL = "UPDATE sequence SET seq_value = ? WHERE seq_name = ?"
INSERT_SQL = "INSERT INTO sequence(seq_name, seq_value) VALUES (?, ?)"
三個內部類分別對應查詢、更新、插入:
SequenceQuery extends MappingSqlQuery:將 ResultSet 映射為 Sequence 領域物件SequenceUpdate extends SqlUpdate:執行遞增更新SequenceInsert extends SqlUpdate:初始化新序列getSequence()public int getSequence(String seq_name) {
Object obj = sequenceQuery.findObject(new Object[]{seq_name});
if (obj == null) {
sequenceInsert.insert(seq_name, 0); // 首次使用自動建立
}
Sequence seq = (Sequence) sequenceQuery.findObject(new Object[]{seq_name});
sequenceUpdate.update(seq.getName(), seq.getValue() + 1);
return seq.getValue() + 1; // 回傳遞增後的值
}
注意:此實現非原子操作,高併發下可能產生重複序號。生產環境建議:
SELECT ... FOR UPDATE 行鎖INCR 指令實現分散式原子計數器afterPropertiesSet()實現 InitializingBean 介面,確保 DataSource 已注入,否則拋出 ApplicationContextException。
public class Sequence {
private String name;
private int value;
// getter/setter 省略
}
seq_value += 100,記憶體中快取 100 號段,減少資料庫往返UPDATE sequence SET seq_value = seq_value + 1 WHERE seq_name = ? AND seq_value = ?,失敗則重試SequenceDaoJdbcImpl 展示了 Spring JDBC 操作物件模式的經典用法,代碼結構清晰,但併發安全性需視業務量級加強。現代專案多改用資料庫原生序列或 Redis,簡化開發並提升效能。