`
huibin
  • 浏览: 739983 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
社区版块
存档分类
最新评论

Spring源代码解析(三):Spring JDBC

阅读更多

下面我们看看Spring JDBC相关的实现,
在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数据 库,在JdbcTemplate为用户程序提供了许多便利的数据库操作方法,比如查询,更新等,而且在Spring中,有许多类似 JdbcTemplate的模板,比如HibernateTemplate等等 - 看来这是Rod.Johnson的惯用手法,一般而言这种Template中都是通过回调函数CallBack类的使用来完成功能的,客户需要在回调接口 中实现自己需要的定制行为,比如使用客户想要用的SQL语句等。不过往往Spring通过这种回调函数的实现已经为我们提供了许多现成的方法供客户使用。 一般来说回调函数的用法采用匿名类的方式来实现,比如:

Java代码 复制代码
  1. JdbcTemplate =  new  JdbcTemplate(datasource);   
  2. jdbcTemplate.execute( new  CallBack(){   
  3.              public  CallbackInterfacedoInAction(){   
  4.                ......   
  5.                 //用户定义的代码或者说Spring 替我们实现的代码   
  6.             }   
  7. }  
    JdbcTemplate = new JdbcTemplate(datasource);
    jdbcTemplate.execute(new CallBack(){
                public CallbackInterfacedoInAction(){
                   ......
                   //用户定义的代码或者说Spring替我们实现的代码
                }
    }


在模板中嵌入的是需要客户化的代码,由Spring来作或者需要客户程序亲自动手完成。下面让我们具体看看在JdbcTemplate中的 代码是怎样完成使命的,我们举JdbcTemplate.execute()为例,这个方法是在JdbcTemplate中被其他方法调用的基本方法之 一,客户程序往往用这个方法来执行基本的SQL语句:

Java代码 复制代码
  1. public  Object execute(ConnectionCallback action)  throws  DataAccessException {   
  2.      //这里得到数据库联接   
  3.     Connection con = DataSourceUtils.getConnection(getDataSource());   
  4.      try  {   
  5.         Connection conToUse = con;   
  6.          //有些特殊的数据库,需要我们使用特别的方法取得 datasource   
  7.          if  ( this .nativeJdbcExtractor !=  null ) {   
  8.              // Extract native JDBC Connection, castable to OracleConnection or the like.   
  9.             conToUse =  this .nativeJdbcExtractor.getNativeConnection(con);   
  10.         }   
  11.          else  {   
  12.              // Create close-suppressing Connection proxy, also preparing returned Statements.   
  13.             conToUse = createConnectionProxy(con);   
  14.         }   
  15.      //这里调用的是传递进来的匿名类的方法,也就是用户程序需要 实现CallBack接口的地方。   
  16.          return  action.doInConnection(conToUse);   
  17.     }   
  18.      catch  (SQLException ex) {   
  19.          //如果捕捉到数据库异常,把数据库联接释放,同时抛 出一个经过Spring转换过的Spring数据库异常,   
  20.          //我们知道,Spring做了一个有意义的工作是把 这些数据库异常统一到自己的异常体系里了。   
  21.         DataSourceUtils.releaseConnection(con, getDataSource());   
  22.         con =  null ;   
  23.          throw  getExceptionTranslator().translate( "ConnectionCallback" , getSql(action), ex);   
  24.     }   
  25.      finally  {   
  26.          //最后不管怎样都会把数据库连接释放   
  27.         DataSourceUtils.releaseConnection(con, getDataSource());   
  28.     }   
  29. }  
    public Object execute(ConnectionCallback action) throws DataAccessException {
        //这里得到数据库联接
        Connection con = DataSourceUtils.getConnection(getDataSource());
        try {
            Connection conToUse = con;
            //有些特殊的数据库,需要我们使用特别的方法取得datasource
            if (this.nativeJdbcExtractor != null) {
                // Extract native JDBC Connection, castable to OracleConnection or the like.
                conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
            }
            else {
                // Create close-suppressing Connection proxy, also preparing returned Statements.
                conToUse = createConnectionProxy(con);
            }
        //这里调用的是传递进来的匿名类的方法,也就是用户程序需要实现CallBack接口的地方。
            return action.doInConnection(conToUse);
        }
        catch (SQLException ex) {
            //如果捕捉到数据库异常,把数据库联接释放,同时抛出一个经过Spring转换过的Spring数据库异常,
            //我们知道,Spring做了一个有意义的工作是把这些数据库异常统一到自己的异常体系里了。
            DataSourceUtils.releaseConnection(con, getDataSource());
            con = null;
            throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
        }
        finally {
            //最后不管怎样都会把数据库连接释放
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }


对于JdbcTemplate中给出的其他方法,比如query,update,execute等的实现,我们看看query():

Java代码 复制代码
  1. public  Object query(PreparedStatementCreator psc,  final  PreparedStatementSetter pss,  final  ResultSetExtractor rse)   
  2.          throws  DataAccessException {   
  3.     ..........   
  4.      //这里调用了我们上面看到的execute()基本方法,然 而这里的回调实现是Spring为我们完成的查询过程   
  5.      return  execute(psc,  new  PreparedStatementCallback() {   
  6.          public  Object doInPreparedStatement(PreparedStatement ps)  throws  SQLException {   
  7.              //准备查询结果集   
  8.             ResultSet rs =  null ;   
  9.              try  {   
  10.              //这里配置SQL参数   
  11.                  if  (pss !=  null ) {   
  12.                     pss.setValues(ps);   
  13.                 }   
  14.            //这里执行的SQL查询   
  15.                 rs = ps.executeQuery();   
  16.                 ResultSet rsToUse = rs;   
  17.                  if  (nativeJdbcExtractor !=  null ) {   
  18.                     rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);   
  19.                 }   
  20.           //返回需要的记录集合   
  21.                  return  rse.extractData(rsToUse);   
  22.             }   
  23.              finally  {   
  24.          //最后关闭查询的纪录集,对数据库连接的释放在 execute()中释放,就像我们在上面分析的看到那样。   
  25.                 JdbcUtils.closeResultSet(rs);   
  26.                  if  (pss  instanceof  ParameterDisposer) {   
  27.                     ((ParameterDisposer) pss).cleanupParameters();   
  28.                 }   
  29.             }   
  30.         }   
  31.     });   
  32. }  
    public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)
            throws DataAccessException {
        ..........
        //这里调用了我们上面看到的execute()基本方法,然而这里的回调实现是Spring为我们完成的查询过程
        return execute(psc, new PreparedStatementCallback() {
            public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
                //准备查询结果集
                ResultSet rs = null;
                try {
                //这里配置SQL参数
                    if (pss != null) {
                        pss.setValues(ps);
                    }
              //这里执行的SQL查询
                    rs = ps.executeQuery();
                    ResultSet rsToUse = rs;
                    if (nativeJdbcExtractor != null) {
                        rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
                    }
             //返回需要的记录集合
                    return rse.extractData(rsToUse);
                }
                finally {
            //最后关闭查询的纪录集,对数据库连接的释放在execute()中释放,就像我们在上面分析的看到那样。
                    JdbcUtils.closeResultSet(rs);
                    if (pss instanceof ParameterDisposer) {
                        ((ParameterDisposer) pss).cleanupParameters();
                    }
                }
            }
        });
    }


辅助类DataSourceUtils来用来对数据库连接进行管理的主要工具,比如打开和关闭数据库连接等基本操作:

Java代码 复制代码
  1. public   static  Connection doGetConnection(DataSource dataSource)  throws  SQLException {   
  2.     //把对数据库连接放到事务管理里面进行管理   
  3.     ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);   
  4.      if  (conHolder !=  null  && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {   
  5.         conHolder.requested();   
  6.          if  (!conHolder.hasConnection()) {   
  7.             logger.debug( "Fetching resumed JDBC Connection from DataSource" );   
  8.             conHolder.setConnection(dataSource.getConnection());   
  9.         }   
  10.          return  conHolder.getConnection();   
  11.     }   
  12.      // 这里得到需要的数据库连接,在配置文件中定义好的。   
  13.     logger.debug( "Fetching JDBC Connection from DataSource" );   
  14.     Connection con = dataSource.getConnection();   
  15.   
  16.      if  (TransactionSynchronizationManager.isSynchronizationActive()) {   
  17.         logger.debug( "Registering transaction synchronization for JDBC Connection" );   
  18.          // Use same Connection for further JDBC actions within the transaction.   
  19.          // Thread-bound object will get removed by synchronization at transaction completion.   
  20.         ConnectionHolder holderToUse = conHolder;   
  21.          if  (holderToUse ==  null ) {   
  22.             holderToUse =  new  ConnectionHolder(con);   
  23.         }   
  24.          else  {   
  25.             holderToUse.setConnection(con);   
  26.         }   
  27.         holderToUse.requested();   
  28.         TransactionSynchronizationManager.registerSynchronization(   
  29.                  new  ConnectionSynchronization(holderToUse, dataSource));   
  30.         holderToUse.setSynchronizedWithTransaction( true );   
  31.          if  (holderToUse != conHolder) {   
  32.             TransactionSynchronizationManager.bindResource(dataSource, holderToUse);   
  33.         }   
  34.     }   
  35.   
  36.      return  con;   
  37. }  
    public static Connection doGetConnection(DataSource dataSource) throws SQLException {
       //把对数据库连接放到事务管理里面进行管理
        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();
        }
        // 这里得到需要的数据库连接,在配置文件中定义好的。
        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;
    }


那我们实际的DataSource对象是怎样得到的?很清楚我们需要在上下文中进行配置:它作为JdbcTemplate父类 JdbcAccessor的属性存在:

Java代码 复制代码
  1. public   abstract   class  JdbcAccessor  implements  InitializingBean {   
  2.   
  3.      /** 这里是我们依赖注入数据库数据源的地方。 */   
  4.      private  DataSource dataSource;   
  5.   
  6.      /** Helper to translate SQL exceptions to DataAccessExceptions */   
  7.      private  SQLExceptionTranslator exceptionTranslator;   
  8.   
  9.      private   boolean  lazyInit =  true ;   
  10.   
  11.     ........   
  12. }  
public abstract class JdbcAccessor implements InitializingBean {

    /** 这里是我们依赖注入数据库数据源的地方。 */
    private DataSource dataSource;

    /** Helper to translate SQL exceptions to DataAccessExceptions */
    private SQLExceptionTranslator exceptionTranslator;

    private boolean lazyInit = true;

    ........
}


而对于DataSource的缓冲池实现,我们通过定义Apache Jakarta Commons DBCP或者C3P0提供的DataSource来完成,然后只要在上下文中配置好就可以使用了。从上面我们看到JdbcTemplate提供了许多简单 查询和更新功能,但是如果需要更高层次的抽象,以及更面向对象的方法来访问数据库。Spring为我们提供了 org.springframework.jdbc.object包,这里面包含了SqlQuery,SqlMappingQuery, SqlUpdate和StoredProcedure等类,这些类都是Spring JDBC应用程序可以使用的主要类,但我们要注意使用这些类的时候,用户需要为他们配置好一个JdbcTemplate作为其基本的操作的实现。
比 如说我们使用MappingSqlQuery来将表数据直接映射到一个对象集合 - 具体可以参考书中的例子
1.我们需要建立 DataSource和sql语句并建立持有这些对象的MappingSqlQuery对象
2.然后我们需要定义传递的 SqlParameter,具体的实现我们在MappingSqlQuery的父类RdbmsOperation中可以找到:

Java代码 复制代码
  1. public   void  declareParameter(SqlParameter param)  throws  InvalidDataAccessApiUsageException {   
  2.     //如果声明已经被编译过,则该声明无效   
  3.     if  (isCompiled()) {   
  4.         throw   new  InvalidDataAccessApiUsageException( "Cannot add parameters once query is compiled" );   
  5.    }   
  6.     //这里对参数值进行声明定义   
  7.     this .declaredParameters.add(param);   
     public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
        //如果声明已经被编译过,则该声明无效
        if (isCompiled()) {
            throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled");
        }
        //这里对参数值进行声明定义
        this.declaredParameters.add(param);
    }


而这个declareParameters维护的是一个列表:

Java代码 复制代码
  1. /** List of SqlParameter objects */   
  2. private  List declaredParameters =  new  LinkedList();  
    /** List of SqlParameter objects */
    private List declaredParameters = new LinkedList();


这个列表在以后compile的过程中会被使用。
3.然后用户程序需要实现MappingSqlQuery的mapRow接口, 将具体的ResultSet数据生成我们需要的对象,这是我们迭代使用的方法。1,2,3步实际上为我们定义好了一个迭代的基本单元作为操作模板。
4. 在应用程序,我们直接调用execute()方法得到我们需要的对象列表,列表中的每一个对象的数据来自于执行SQL语句得到记录集的每一条记录,事实上 执行的execute在父类SqlQuery中起作用:

Java代码 复制代码
  1. public  List executeByNamedParam(Map paramMap, Map context)  throws  DataAccessException {   
  2.     validateNamedParameters(paramMap);   
  3.     Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap);   
  4.     RowMapper rowMapper = newRowMapper(parameters, context);   
  5.     String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(),  new  MapSqlParameterSource(paramMap));   
  6.      //我们又看到了JdbcTemplate,这里使用 JdbcTemplate来完成对数据库的查询操作,所以我们说JdbcTemplate是基本的操作类。   
  7.       return  getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);   
  8. }  
    public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException {
        validateNamedParameters(paramMap);
        Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap);
        RowMapper rowMapper = newRowMapper(parameters, context);
        String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap));
        //我们又看到了JdbcTemplate,这里使用JdbcTemplate来完成对数据库的查询操作,所以我们说JdbcTemplate是基本的操作类。
         return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);
    }


在这里我们可以看到template模式的精彩应用和对JdbcTemplate的灵活使用。通过使用它,我们免去了手工迭代 ResultSet并将其中的数据转化为对象列表的重复过程。在这里我们只需要定义SQL语句和SqlParameter - 如果需要的话,往往SQL语句就常常能够满足我们的要求了。这是灵活使用JdbcTemplate的一个很好的例子。
Spring还为其他数据 库操作提供了许多服务,比如使用SqlUpdate插入和更新数据库,使用UpdatableSqlQuery更新ResultSet,生成主键,调用存 储过程等。
书中还给出了对BLOB数据和CLOB数据进行数据库操作的例子:
对BLOB数据的操作通过LobHander来完成,通 过调用JdbcTemplate和RDBMS都可以进行操作:
在JdbcTemplate中,具体的调用可以参考书中的例子 - 是通过以下调用起作用的:

Java代码 复制代码
  1. public  Object execute(String sql, PreparedStatementCallback action)  throws  DataAccessException {   
  2.      return  execute( new  SimplePreparedStatementCreator(sql), action);   
  3. }  
    public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {
        return execute(new SimplePreparedStatementCreator(sql), action);
    }


然后通过对实现PreparedStatementCallback接口的 AbstractLobCreatingPreparedStatementCallback的回调函数来完成:

Java代码 复制代码
  1. public   final  Object doInPreparedStatement(PreparedStatement ps)  throws  SQLException, DataAccessException {   
  2.     LobCreator lobCreator =  this .lobHandler.getLobCreator();   
  3.      try  {   
  4.          //这是一个模板方法,具体需要由客户程序实现   
  5.         setValues(ps, lobCreator);   
  6.          return   new  Integer(ps.executeUpdate());   
  7.     }   
  8.      finally  {   
  9.         lobCreator.close();   
  10.     }   
  11. }   
  12. //定义的需要客户程序实现的虚函数   
  13. protected   abstract   void  setValues(PreparedStatement ps, LobCreator lobCreator)   
  14.          throws  SQLException, DataAccessException;  
    public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
        LobCreator lobCreator = this.lobHandler.getLobCreator();
        try {
            //这是一个模板方法,具体需要由客户程序实现
            setValues(ps, lobCreator);
            return new Integer(ps.executeUpdate());
        }
        finally {
            lobCreator.close();
        }
    }
    //定义的需要客户程序实现的虚函数
    protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)
            throws SQLException, DataAccessException;


而我们注意到setValues()是一个需要实现的抽象方法,应用程序通过实现setValues来定义自己的操作 - 在setValues中调用lobCreator.setBlobAsBinaryStrem()。让我们看看具体的BLOB操作在LobCreator 是怎样完成的,我们一般使用DefaultLobCreator作为BLOB操作的驱动:

Java代码 复制代码
  1. public   void  setBlobAsBinaryStream(   
  2.         PreparedStatement ps,  int  paramIndex, InputStream binaryStream,  int  contentLength)   
  3.          throws  SQLException {   
  4.      //通过JDBC来完成对BLOB数据的操作,对 Oracle,Spring提供了OracleLobHandler来支持BLOB操作。   
  5.     ps.setBinaryStream(paramIndex, binaryStream, contentLength);   
  6.     ........   
  7. }  
        public void setBlobAsBinaryStream(
                PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength)
                throws SQLException {
            //通过JDBC来完成对BLOB数据的操作,对Oracle,Spring提供了OracleLobHandler来支持BLOB操作。
            ps.setBinaryStream(paramIndex, binaryStream, contentLength);
            ........
        }


上面提到的是零零碎碎的Spring JDBC使用的例子,可以看到使用Spring JDBC可以帮助我们完成许多数据库的操作。Spring对数据库操作最基本的服务是通过JdbcTeamplate和他常用的回调函数来实现的,在此之 上,又提供了许多RMDB的操作来帮助我们更便利的对数据库的数据进行操作 - 注意这里没有引入向Hibernate这样的O/R方案。对这些O/R方案的支持,Spring由其他包来完成服务。
书中还提到关于 execute和update方法之间的区别,update方法返回的是受影响的记录数目的一个计数,并且如果传入参数的话,使用的是 java.sql.PreparedStatement,而execute方法总是使用 java.sql.Statement,不接受参数,而且他不返回受影响记录的计数,更适合于创建和丢弃表的语句,而update方法更适合于插入,更新 和删除操作,这也是我们在使用时需要注意的。

分享到:
评论
1 楼 annybz 2013-09-06  
说的好抽象 为什么代码都有两遍。

这个感觉没有第一篇 和第二篇 写的好了

相关推荐

    Spring源代码解析.rar

    Spring源代码解析3:Spring JDBC .doc Spring源代码解析4:Spring MVC .doc Spring源代码解析5:Spring AOP获取Proxy .doc Spring源代码解析6:Spring声明式事务处理 .doc Spring源代码解析7:Spring AOP中对拦截器...

    Spring 源代码解析

    Spring源代码解析3:Spring JDBC ; Spring源代码解析4:Spring MVC ;Spring源代码解析5:Spring AOP获取Proxy;Spring源代码解析6:Spring声明式事务处理 ; Spring源代码解析7:Spring AOP中对拦截器调用的实现...

    Spring源代码解析

    Spring源代码解析(三):Spring JDBC Spring源代码解析(四):Spring MVC Spring源代码解析(五):Spring AOP获取Proxy Spring源代码解析(六):Spring声明式事务处理 Spring源代码解析(七):Spring AOP中对拦截器...

    Spring源代码解析3:SpringJDBC[归纳].pdf

    Spring源代码解析3:SpringJDBC[归纳].pdf

    Spring源代码解析(三):Spring_JDBC.doc

    Spring源代码解析(三):Spring_JDBC.doc

    springyuanmaaping.zip

    Spring源代码解析3:Spring JDBC ; Spring源代码解析4:Spring MVC ;Spring源代码解析5:Spring AOP获取Proxy;Spring源代码解析6:Spring声明式事务处理 ; Spring源代码解析7:Spring AOP中对拦截器调用的实现...

    spring源码分析(1-10)

    Spring源代码分析(三):Spring JDBC Spring源代码解析(四):Spring MVC Spring源代码解析(五):Spring AOP获取Proxy Spring源代码解析(六):Spring声明式事务处理 Spring源代码解析(七):Spring AOP中对拦截器...

    spring源码分析

    3.Spring源代码解析(三):Spring JDBC 4.Spring源代码解析(四):Spring MVC 5.Spring源代码解析(五):Spring AOP获取Proxy 6. Spring源代码解析(六):Spring声明式事务处理 7. Spring源代码解析(七):...

    Spring源码学习文档,绝对值得好好研究~~

    Spring源代码分析(三):Spring JDBC.doc Spring源代码解析(四):Spring MVC.doc Spring源代码解析(五):Spring AOP获取Proxy.doc Spring源代码解析(六):Spring声明式事务处理.doc Spring源代码解析(七):Spring...

    mysql-jdbc-Spring源代码分析

    mysql-jdbc-Spring源代码分析 mysql-jdbc-Spring源代码分析 mysql-jdbc-Spring源代码分析

    spring-jdbc-5.3.15-API文档-中文版.zip

    赠送源代码:spring-jdbc-5.3.15-sources.jar; 赠送Maven依赖信息文件:spring-jdbc-5.3.15.pom; 包含翻译后的API文档:spring-jdbc-5.3.15-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework:...

    JavaEE源代码 spring-jdbc

    JavaEE源代码 spring-jdbcJavaEE源代码 spring-jdbcJavaEE源代码 spring-jdbcJavaEE源代码 spring-jdbcJavaEE源代码 spring-jdbcJavaEE源代码 spring-jdbcJavaEE源代码 spring-jdbcJavaEE源代码 spring-jdbcJavaEE源...

    spring源代码解析

    对spring源代码进行了深入的解析,特别是对IOC容器,,spring事物控制,AOP拦截器的实现以及springJDBC和Hibernate的实现进行了详细的分析, 是项目开发中不可多得的好资料!

    spring-jdbc-5.3.7-API文档-中文版.zip

    赠送源代码:spring-jdbc-5.3.7-sources.jar; 赠送Maven依赖信息文件:spring-jdbc-5.3.7.pom; 包含翻译后的API文档:spring-jdbc-5.3.7-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework:...

    spring-jdbc-4.3.20.RELEASE-API文档-中文版.zip

    赠送源代码:spring-jdbc-4.3.20.RELEASE-sources.jar; 包含翻译后的API文档:spring-jdbc-4.3.20.RELEASE-javadoc-API文档-中文(简体)版.zip 对应Maven信息:groupId:org.springframework,artifactId:spring-...

    spring-jdbc-5.3.10-API文档-中文版.zip

    赠送源代码:spring-jdbc-5.3.10-sources.jar; 赠送Maven依赖信息文件:spring-jdbc-5.3.10.pom; 包含翻译后的API文档:spring-jdbc-5.3.10-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.springframework:...

    spring-jdbc-5.1.3.RELEASE-API文档-中文版.zip

    赠送源代码:spring-jdbc-5.1.3.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-jdbc-5.1.3.RELEASE.pom; 包含翻译后的API文档:spring-jdbc-5.1.3.RELEASE-javadoc-API文档-中文(简体)版.zip; Maven坐标:...

    spring-jdbc-5.0.8.RELEASE-API文档-中英对照版.zip

    赠送源代码:spring-jdbc-5.0.8.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-jdbc-5.0.8.RELEASE.pom; 包含翻译后的API文档:spring-jdbc-5.0.8.RELEASE-javadoc-API文档-中文(简体)-英语-对照版.zip; ...

    spring-jdbc-5.3.7-API文档-中英对照版.zip

    赠送源代码:spring-jdbc-5.3.7-sources.jar; 赠送Maven依赖信息文件:spring-jdbc-5.3.7.pom; 包含翻译后的API文档:spring-jdbc-5.3.7-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.spring...

Global site tag (gtag.js) - Google Analytics