深刻解析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

相关文章
相关标签/搜索