Oracle 使用 SEQUENCE 產生主鍵;MySQL 採用列屬性 AUTO_INCREMENT。插入後需獲取剛生成的 ID,MySQL 提供 SELECT @@IDENTITY(或 LAST_INSERT_ID())。
官方文檔:
SELECT @@IDENTITY返回**當前連接**上一次INSERT產生的自增值。其他連接的插入**不影響**本連接的返回值。
因此,線程隔離的連接池(每線程一連接)天然安全。Spring、MyBatis、Hibernate 默認使用 ThreadLocal 綁定連接,多線程環境下各線程獲取到的均為自己插入的 ID。
Connection 對象傳遞給多線程並發使用(極差實踐)。Statement.RETURN_GENERATED_KEYS 配合 getGeneratedKeys() 可完全避免此問題(JDBC 標準方式)。String sql = "INSERT INTO users(name, email) VALUES(?, ?)";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, "張三");
ps.setString(2, "zhangsan@example.com");
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) {
long generatedId = rs.getLong(1);
System.out.println("新用戶 ID: " + generatedId);
}
}
}
CREATE TABLE wfo_seq ( name VARCHAR(50) NOT NULL PRIMARY KEY, current_value INT NOT NULL, increment INT NOT NULL DEFAULT 1 ) ENGINE=InnoDB;
-- 當前值
CREATE FUNCTION currval(seq_name VARCHAR(50)) RETURNS INT
BEGIN
DECLARE v INT DEFAULT 0;
SELECT current_value INTO v FROM wfo_seq WHERE name = seq_name;
RETURN v;
END;
-- 下一個值(原子更新+返回)
CREATE FUNCTION nextval(seq_name VARCHAR(50)) RETURNS INT
BEGIN
UPDATE wfo_seq SET current_value = current_value + increment WHERE name = seq_name;
IF ROW_COUNT() = 0 THEN
INSERT INTO wfo_seq(name, current_value, increment) VALUES(seq_name, 1, 1);
RETURN 1;
END IF;
RETURN currval(seq_name);
END;
-- 設置值
CREATE FUNCTION setval(seq_name VARCHAR(50), value INT) RETURNS INT
BEGIN
UPDATE wfo_seq SET current_value = value WHERE name = seq_name;
RETURN currval(seq_name);
END;
SELECT setval('TestSeq', 10); -- 初始化
SELECT currval('TestSeq'); -- 10
SELECT nextval('TestSeq'); -- 11
SELECT nextval('TestSeq'); -- 12
CREATE SEQUENCE(兼容 Oracle 語法),可作為遷移過渡方案。