MySQL 表 id INT AUTO_INCREMENT,Spring JdbcTemplate 執行 INSERT 後需獲取新生成的 ID。直接 SELECT LAST_INSERT_ID() 在高並發下不安全,且 Spring 連接獲取方式 (DataSourceUtils.getConnection) 導致非同一連接。
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 的 PSGeneratedKeyHolder 實現 KeyHolder,getKey() 返回 NumbergetKeys() 返回 Map,批量插入用 getKeyList() 返回 List<Map><bean id="incrementer" class="org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer">
<property name="incrementerName" value="SEQ_POST_ID"/>
<property name="dataSource" ref="dataSource"/>
</bean>
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>
@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());
}
| 維度 | GeneratedKeyHolder | Incrementer (應用層) |
|---|---|---|
| 依賴 | JDBC 3.0 驅動支援 | 數據庫序列/模擬表 |
| 並發安全 | 驅動層面保證 | 表鎖/序列原子性保證 |
| 可移植性 | 需驅動支援 | 高 (統一介面) |
| 性能 | 單次往返 | 緩存批量取號極快 |
| 適用場景 | 單表自增鍵、驅動成熟 | 跨庫、高並發、無自增鍵 |
使用 SqlUpdate 時需顯式 setReturnGeneratedKeys(true) 才能配合 KeyHolder 工作。