深刻解析Spring架構與設計原理-數據庫的操做實現

關於Spring JDBC
仍是從Spring JDBC提及吧,雖然如今應用不少都是直接使用Hibernate或者其餘的ORM工具。但JDBC畢竟仍是很基本的,其中的JdbcTemplate就是咱們常常使用的,好比JDBCTemplate的execute方法,就是一個基本的方法,在這個方法的實現中,能夠看到對數據庫操做的基本過程。java


//execute方法執行的是輸入的sql語句  
public void execute(final String sql) throws DataAccessException {  
    if (logger.isDebugEnabled()) {  
        logger.debug("Executing SQL statement [" + sql + "]");  
    }  
    class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {  
        public Object doInStatement(Statement stmt) throws SQLException {  
            stmt.execute(sql);  
            return null;  
        }  
        public String getSql() {  
            return sql;  
        }  
    }  
    execute(new ExecuteStatementCallback());  
}  
//這是使用java.sql.Statement處理靜態SQL語句的方法  
public <T> T execute(StatementCallback<T> action) throws DataAccessException {  
    Assert.notNull(action, "Callback object must not be null");  
    //這裏取得數據庫的Connection,這個數據庫的Connection已經在Spring的事務管理之下  
    Connection con = DataSourceUtils.getConnection(getDataSource());  
    Statement stmt = null;  
    try {  
        Connection conToUse = con;  
        if (this.nativeJdbcExtractor != null &&  
                this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {  
            conToUse = this.nativeJdbcExtractor.getNativeConnection(con);  
        }  
        //建立Statement  
        stmt = conToUse.createStatement();  
        applyStatementSettings(stmt);  
        Statement stmtToUse = stmt;  
        if (this.nativeJdbcExtractor != null) {  
            stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);  
        }  
        //這裏調用回調函數  
        T result = action.doInStatement(stmtToUse);  
        handleWarnings(stmt);  
        return result;  
    }  
    catch (SQLException ex) {  
        // Release Connection early, to avoid potential connection pool deadlock  
        // in the case when the exception translator hasn't been initialized yet.  
        //若是捕捉到數據庫異常,把數據庫Connection釋放,同時拋出一個通過Spring轉換過的Spring數據庫異常  
        //Spring作了一項有意義的工做,就是把這些數據庫異常統一到本身的異常體系裏了  
        JdbcUtils.closeStatement(stmt);  
        stmt = null;  
        DataSourceUtils.releaseConnection(con, getDataSource());  
        con = null;  
        throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);  
    }  
    finally {  
        JdbcUtils.closeStatement(stmt);  
        //釋放數據庫connection  
        DataSourceUtils.releaseConnection(con, getDataSource());  
    }  
}  
spring


在使用數據庫的時候,有一個很重要的地方就是對數據庫鏈接的管理,在這裏,是由DataSourceUtils來完成的。Spring經過這個輔助類來對數據的Connection進行管理。好比經過它來完成打開和關閉Connection等操做。DataSourceUtils對這些數據庫Connection管理的實現, 如如下代碼所示。sql


//這是取得數據庫鏈接的調用,實現是經過調用doGetConnection完成的,這裏執行了異常的轉換操做  
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {  
    try {  
        return doGetConnection(dataSource);  
    }  
    catch (SQLException ex) {  
        throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);  
    }  
}  
public static Connection doGetConnection(DataSource dataSource) throws SQLException {  
    Assert.notNull(dataSource, "No DataSource specified");  
    //把對數據庫的Connection放到事務管理中進行管理,這裏使用TransactionSynchronizationManager中定義的ThreadLocal變量來和線程綁定數據庫鏈接  
    //若是在TransactionSynchronizationManager中已經有與當前線程綁定數據庫鏈接,那就直接取出來使用  
    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
    if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {  
        conHolder.requested();  
        if (!conHolder.hasConnection()) {  
            logger.debug("Fetching resumed JDBC Connection from DataSource");  
            conHolder.setConnection(dataSource.getConnection());  
        }  
        return conHolder.getConnection();  
    }  
    // Else we either got no holder or an empty thread-bound holder here.  
    // 這裏獲得須要的數據庫Connection,在Bean配置文件中定義好的,  
    // 同時最後把新打開的數據庫Connection經過TransactionSynchronizationManager和當前線程綁定起來。  
    logger.debug("Fetching JDBC Connection from DataSource");  
    Connection con = dataSource.getConnection();  
  
    if (TransactionSynchronizationManager.isSynchronizationActive()) {  
        logger.debug("Registering transaction synchronization for JDBC Connection");  
        // Use same Connection for further JDBC actions within the transaction.  
        // Thread-bound object will get removed by synchronization at transaction completion.  
        ConnectionHolder holderToUse = conHolder;  
        if (holderToUse == null) {  
            holderToUse = new ConnectionHolder(con);  
        }  
        else {  
            holderToUse.setConnection(con);  
        }  
        holderToUse.requested();  
        TransactionSynchronizationManager.registerSynchronization(  
                new ConnectionSynchronization(holderToUse, dataSource));  
        holderToUse.setSynchronizedWithTransaction(true);  
        if (holderToUse != conHolder) {  
            TransactionSynchronizationManager.bindResource(dataSource, holderToUse);  
        }  
    }  
    return con;  
}  
數據庫


關於數據庫操做類RDBMS
從JdbcTemplate中,咱們看到,他提供了許多簡單查詢和更新的功能。可是,若是須要更高層次的抽象,以及更面向對象的方法來訪問數據庫,Spring爲咱們提供了org.springframework.jdbc.object包,裏面包含了SqlQuery、SqlMappingQuery、SqlUpdate和StoredProcedure等類,這些類都是Spring JDBC應用程序可使用的。但要注意,在使用這些類時須要爲它們配置好JdbcTemplate做爲其基本的操做實現,由於在它們的功能實現中,對數據庫操做的那部分實現基本上仍是依賴於JdbcTemplate來完成的。

好比,對MappingSqlQuery使用的過程,是很是簡潔的;在設計好數據的映射代碼以後,查詢獲得的記錄已經按照前面的設計轉換爲對象List了,一條查詢記錄對應於一個數據對象,能夠把數據庫的數據記錄直接映射成Java對象在程序中使用,同時又可避免使用第三方ORM工具的配置,對於簡單的數據映射場合是很是方便的;在mapRow方法的實現中提供的數據轉換規則,和咱們使用Hibernate時,Hibernate的hbm文件起到的做用是很是相似的。這個MappingSqlQuery須要的對設置進行compile,這些compile是這樣完成的,如如下代碼所示:微信


protected final void compileInternal() {  
    //這裏是對參數的compile過程,全部的參數都在getDeclaredParameters裏面,生成了一個PreparedStatementCreatorFactory  
    this.preparedStatementFactory = new PreparedStatementCreatorFactory(getSql(), getDeclaredParameters());  
    this.preparedStatementFactory.setResultSetType(getResultSetType());  
    this.preparedStatementFactory.setUpdatableResults(isUpdatableResults());  
    this.preparedStatementFactory.setReturnGeneratedKeys(isReturnGeneratedKeys());  
    if (getGeneratedKeysColumnNames() != null) {  
        this.preparedStatementFactory.setGeneratedKeysColumnNames(getGeneratedKeysColumnNames());  
    }  
his.preparedStatementFactory.setNativeJdbcExtractor(getJdbcTemplate().getNativeJdbcExtractor());  
    onCompileInternal();  
session


在執行查詢時,執行的其實是SqlQuery的executeByNamedParam方法,這個方法須要完成的工做包括配置SQL語句,配置數據記錄到數據對象的轉換的RowMapper,而後使用JdbcTemplate來完成數據的查詢,並啓動數據記錄到Java數據對象的轉換,如如下代碼所示:app


public List<T> executeByNamedParam(Map<String, ?> paramMap, Map context) throws DataAccessException {  
    validateNamedParameters(paramMap);  
    //獲得須要執行的SQL語句  
    ParsedSql parsedSql = getParsedSql();  
    MapSqlParameterSource paramSource = new MapSqlParameterSource(paramMap);  
    String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);  
    //配置好SQL語句須要的Parameters及rowMapper,這個rowMapper完成數據記錄到對象的轉換  
    Object[] params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, getDeclaredParameters());  
    RowMapper<T> rowMapper = newRowMapper(params, context);  
    //咱們又看到了JdbcTemplate,這裏使用JdbcTemplate來完成對數據庫的查詢操做,因此咱們說JdbcTemplate是很是基本的操做類  
        return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, params), rowMapper);  
}  
ide


在Spring對JDBC的操做中,基本上是對JDBC/Hibernate基礎上API的封裝。這些封裝能夠直接使用,也能夠在IoC容器中配置好了再使用,當結合IoC容器的基礎上進行使用的時候,能夠看到許多和事務管理相關的處理部分,都是很是值得學習的,在那裏,能夠看到對數據源的管理 - Hibernate中session的管理,與線程的結合等等。函數

更多內容請關注微信公衆號:IT哈哈(it_haha)工具

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

相關文章
相關標籤/搜索