首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

Spring JDBC 獲取自增主鍵最佳實踐:GeneratedKeyHolder 與 DataFieldMaxValueIncrementer

     編輯:Oracle 資料庫相關知識

問題場景

MySQL 表 id INT AUTO_INCREMENT,Spring JdbcTemplate 執行 INSERT 後需獲取新生成的 ID。直接 SELECT LAST_INSERT_ID() 在高並發下不安全,且 Spring 連接獲取方式 (DataSourceUtils.getConnection) 導致非同一連接。

方案一:JDBC 3.0 + GeneratedKeyHolder (推薦)

public void addForum(final Forum forum) {
  final String sql = "INSERT INTO t_forum(forum_name, forum_desc) VALUES(?,?)";
  KeyHolder keyHolder = new GeneratedKeyHolder();

  getJdbcTemplate().update(new PreparedStatementCreator() {
    @Override
    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
      PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
      ps.setString(1, forum.getForumName());
      ps.setString(2, forum.getForumDesc());
      return ps;
    }
  }, keyHolder);

  forum.setForumId(keyHolder.getKey().intValue());  // 單行單鍵直接取值
}
  • PreparedStatementCreator 返回帶 RETURN_GENERATED_KEYS 的 PS
  • GeneratedKeyHolder 實現 KeyHolder,getKey() 返回 Number
  • 復合鍵用 getKeys() 返回 Map,批量插入用 getKeyList() 返回 List<Map>

方案二:應用層主鍵生成器 (跨數據庫、高並發安全)

Oracle Sequence

<bean id="incrementer" class="org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer">
  <property name="incrementerName" value="SEQ_POST_ID"/>
  <property name="dataSource" ref="dataSource"/>
</bean>

MySQL 模擬序列表

CREATE TABLE t_post_id (sequence_id INT) ENGINE=MYISAM;
INSERT INTO t_post_id VALUES (0);

<bean id="incrementer" class="org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer">
  <property name="incrementerName" value="t_post_id"/>
  <property name="columnName" value="sequence_id"/>
  <property name="cacheSize" value="10"/>  <!-- 批量取號減少 DB 往返 -->
  <property name="dataSource" ref="dataSource"/>
</bean>

DAO 中使用

@Autowired private DataFieldMaxValueIncrementer incrementer;

public void addPost(final Post post) {
  int id = incrementer.nextIntValue();  // 線程安全
  getJdbcTemplate().update(
    "INSERT INTO t_post(id, title, content) VALUES(?,?,?)",
    id, post.getTitle(), post.getContent());
}

對比總結

維度GeneratedKeyHolderIncrementer (應用層)
依賴JDBC 3.0 驅動支援數據庫序列/模擬表
並發安全驅動層面保證表鎖/序列原子性保證
可移植性需驅動支援高 (統一介面)
性能單次往返緩存批量取號極快
適用場景單表自增鍵、驅動成熟跨庫、高並發、無自增鍵

補充

使用 SqlUpdate 時需顯式 setReturnGeneratedKeys(true) 才能配合 KeyHolder 工作。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved