Spring JDBC 的核心思想是:將不變的資源管理(獲取連接、創建 Statement、關閉資源、異常轉換)固化在模板類中,將變化的 SQL 構建、參數綁定、結果集映射暴露為回調接口供用戶實現。這正是 JdbcTemplate 及其姊妹類(NamedParameterJdbcTemplate、SimpleJdbcInsert 等)的設計精髓。
public T execute(ConnectionCallback action) throws DataAccessException {
Connection con = DataSourceUtils.getConnection(getDataSource());
try {
Connection conToUse = (nativeJdbcExtractor != null)
? nativeJdbcExtractor.getNativeConnection(con)
: createConnectionProxy(con); // 代理 Connection,自動關閉 Statement
return action.doInConnection(conToUse); // 用戶回調
} catch (SQLException ex) {
DataSourceUtils.releaseConnection(con, getDataSource());
throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
} finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
關鍵點:
DataSourceUtils.getConnection():事務感知獲取連接(優先從 TransactionSynchronizationManager 取綁定連接)。createConnectionProxy():返回 CloseSuppressingConnectionProxy,防止用戶代碼誤關閉連接,並自動關閉創建的 Statement/ResultSet。SQLExceptionTranslator 將廠商錯誤碼映射為 Spring 統一的 DataAccessException 體系(BadSqlGrammarException、DataIntegrityViolationException 等)。public List query(String sql, RowMapper rowMapper, Object... args) {
return query(new PreparedStatementCreatorFactory(sql, args).newPreparedStatementCreator(),
ps -> { /* 參數綁定由工廠完成 */ },
rs -> { /* RowMapper 逐行映射 */ });
}
核心回調接口體系:
| 接口 | 職責 | 典型實現 |
|---|---|---|
| PreparedStatementCreator | 創建 PreparedStatement(含 SQL 與參數) | PreparedStatementCreatorFactory |
| PreparedStatementSetter | 綁定參數 | ArgumentPreparedStatementSetter |
| ResultSetExtractor | 提取整個結果集(單值/列表/Map) | RowMapperResultSetExtractor |
| RowMapper | 映射單行數據 | BeanPropertyRowMapper、Lambda |
| RowCallbackHandler | 逐行處理(不返回結果) | 寫入文件、流式處理 |
在 org.springframework.jdbc.object 包中,Spring 提供了更高層級的抽象,將 SQL、參數、映射邏輯封裝為可復用的 Java 對象:
public class ActorMappingQuery extends MappingSqlQuery {
public ActorMappingQuery(DataSource ds) {
super(ds, "SELECT id, name FROM actor WHERE id = ?");
declareParameter(new SqlParameter("id", Types.INTEGER));
compile(); // 必須調用,凍結參數元數據
}
@Override
protected Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getInt("id"));
actor.setName(rs.getString("name"));
return actor;
}
}
// 使用
ActorMappingQuery query = new ActorMappingQuery(dataSource);
List actors = query.execute(Collections.singletonMap("id", 1));
優勢:SQL 與映射邏輯內聚、編譯期檢查參數、線程安全可復用、支援命名參數。
SqlUpdate:封裝 INSERT/UPDATE/DELETE,update(Object... params) 返回影響行數。UpdatableSqlQuery:可滾動/可更新結果集,罕用。public class GetActorProc extends StoredProcedure {
public GetActorProc(DataSource ds) {
super(ds, "get_actor");
declareParameter(new SqlParameter("in_id", Types.INTEGER));
declareParameter(new SqlOutParameter("out_name", Types.VARCHAR));
compile();
}
public String execute(int id) {
Map out = execute(new MapSqlParameterSource("in_id", id));
return (String) out.get("out_name");
}
}
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (holder != null && (holder.hasConnection() || holder.isSynchronizedWithTransaction())) {
holder.requested();
if (!holder.hasConnection()) holder.setConnection(dataSource.getConnection());
return holder.getConnection();
}
Connection con = dataSource.getConnection();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
holder = (holder != null) ? holder : new ConnectionHolder(con);
holder.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holder, dataSource));
holder.setSynchronizedWithTransaction(true);
TransactionSynchronizationManager.bindResource(dataSource, holder);
}
return con;
}
這段代碼實現了:
ConnectionSynchronization,在事務提交/回滾時自動清理綁定。統一 Oracle BLOB/CLOB、MySQL BLOB/TEXT、PostgreSQL BYTEA/TEXT 的差異:
jdbcTemplate.execute("INSERT INTO docs (content) VALUES (?)",
new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
@Override
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
lobCreator.setBlobAsBinaryStream(ps, 1, inputStream, contentLength);
}
});
DefaultLobCreator 內部調用 ps.setBinaryStream(),Oracle 場景下 OracleLobHandler 會使用 oracle.sql.BLOB API。
| 特性 | execute() | update() |
|---|---|---|
| Statement 類型 | java.sql.Statement | java.sql.PreparedStatement |
| 參數支持 | 不支持 | 支持 |
| 返回值 | boolean (是否有 ResultSet) | int (影響行數) |
| 適用場景 | DDL、無參查詢 | DML、參數化查詢 |
jdbcTemplate.batchUpdate(sql, batchArgs) 顯著提升性能。DataSourceUtils 實現事務傳播。DataAccessException 子類,而非 SQLException。Spring JDBC 不是 ORM,但通過「模板 + 回調」將 JDBC 的樣板代碼壓縮到極致,同時保留了 SQL 的完全控制權,是「輕量級數據訪問」的經典範例。