首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring JDBC 核心源碼解析:JdbcTemplate 回調模板模式與 RDBMS 操作對象體系

     編輯:Java 相關知識

設計哲學:模板方法模式 + 回調函數 = 最大化復用與最小化樣板代碼

Spring JDBC 的核心思想是:將不變的資源管理(獲取連接、創建 Statement、關閉資源、異常轉換)固化在模板類中,將變化的 SQL 構建、參數綁定、結果集映射暴露為回調接口供用戶實現。這正是 JdbcTemplate 及其姊妹類(NamedParameterJdbcTemplate、SimpleJdbcInsert 等)的設計精髓。

JdbcTemplate.execute():所有操作的基石

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 等)。

查詢操作模板:query() 方法族

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逐行處理(不返回結果)寫入文件、流式處理

RDBMS 操作對象體系:面向對象的數據庫操作封裝

在 org.springframework.jdbc.object 包中,Spring 提供了更高層級的抽象,將 SQL、參數、映射邏輯封裝為可復用的 Java 對象:

SqlQuery / MappingSqlQuery:查詢操作對象

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 / UpdatableSqlQuery:修改操作對象

  • SqlUpdate:封裝 INSERT/UPDATE/DELETE,update(Object... params) 返回影響行數。
  • UpdatableSqlQuery:可滾動/可更新結果集,罕用。

StoredProcedure:存儲過程調用對象

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");
    }
}

DataSourceUtils:事務感知連接管理的核心

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;
}

這段代碼實現了:

  1. 事務傳播:同一事務內複用同一 Connection。
  2. 事務同步:註冊 ConnectionSynchronization,在事務提交/回滾時自動清理綁定。
  3. 連接池兼容:不干擾底層 DataSource 的池化邏輯。

LOB 大對象處理:LobHandler 抽象與 LobCreator 模板

統一 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 vs update 關鍵差異

特性execute()update()
Statement 類型java.sql.Statementjava.sql.PreparedStatement
參數支持不支持支持
返回值boolean (是否有 ResultSet)int (影響行數)
適用場景DDL、無參查詢DML、參數化查詢

總結與最佳實踐

  • 優先使用 JdbcTemplate:簡單查詢/更新,代碼簡潔,自動資源管理。
  • 複雜/復用查詢用 MappingSqlQuery:SQL 與映射邏輯內聚,編譯期檢查,線程安全。
  • 批量操作用 batchUpdate():jdbcTemplate.batchUpdate(sql, batchArgs) 顯著提升性能。
  • 命名參數用 NamedParameterJdbcTemplate:SQL 可讀性強,避免佔位符順序錯誤。
  • 事務邊界由 @Transactional 控制:JdbcTemplate 無事務感知,依賴 DataSourceUtils 實現事務傳播。
  • 異常處理信任 Spring 轉換:捕獲 DataAccessException 子類,而非 SQLException。

Spring JDBC 不是 ORM,但通過「模板 + 回調」將 JDBC 的樣板代碼壓縮到極致,同時保留了 SQL 的完全控制權,是「輕量級數據訪問」的經典範例。

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