学习Spring JDBC Framework

       这里记录我对Spring JDBC框架的学习。由于Spring JDBC和我之前做的工作有很多共同之处,学习经典Framework的设计,取长补短,为我所用。
        在这里,先佩服一下Rod JohnSon,他对数据库,JDBC的理解非常深。看Spring jdbc框架的设计和源代码,带给了我很多以前没有想到的东西。
       我们知道,Spring JDBC的主要目标是为了简化JDBC的编程,方便我们构建健壮的应用程序。这里,它的一个基本设计理念,就是将JDBC编程中变化的和不变化的分开。
        在JDBC中,什么是变化的?毫无疑问,SQL语句是变化的。那什么是不变化的?正确的使用JDBC的方式是不变化的。
先看一段代码。(自己的代码就不拿来做示范了 ,先看Rod书吧)

java 代码
      从上面看,什么是不变的。首先,咱们这个使用JDBC的方式是良好的,正确的,也是不变的,也就是workflow不变。其次,这里头的很多操作是不变的,比如说:关闭资源,处理异常。
        什么是变的?设置PreparedStament的参数是变化的,利用PreparedStatement做什么是变化的。
       还有什么是变的?取得Connection可能是变化的,我们可以从ConnectionPool中取,也可以裸从Database取。
       还有什么是变的?在主工作流之外,还可以对PreparedStament设置一些属性。比如fetchSize等。
       还有什么是变的?解析ResultSet是变的。但是可以抽象,都是从结果集中取得你想要的东西。
     
       很好。经过分析,我们会自然而然的想到Template设计模式。用模板方法来描述我们的工作流。对于固定的操作,我们会把它建模为一些帮助类,利用这些类来完成固定操作,这些操作在Template方法中被调用。
       对于哪些可以变的方法。我们也发现,其实它要实现的功能是一样的。抽象起来,我们可以用一些接口来描述这些功能。比如说数据库连接管理的功能。
      设计取决于我们考虑问题的深度,以及我们对过程划分的粒度。

       下面,我们阅读Spring JDBC Template的代码吧。好好享受一下。下面几个接口是对变化的部分进行建模:)
      
接口:创建PreparedStatement。根据Connection来创建PreparedStatement。


 
使用方法就是:


给PreparedStatement设置参数。是对PreparedStatmentCreator的设置ps值的一个补充。

对ResultSet进行处理。还有具体的子类。
 
  1. public   interface  RowCallbackHandler {  
  2.      void  processRow(ResultSet rs)  throws  SQLException;  
  3.    }  

       使用方式:
 
  1.  RowCallbackHandler rch =  new  RowCallbackHandler() {  
  2.    public   void  processRow(ResultSet rs)  throws  SQLException {  
  3.      int  seatId = rs.getInt( 1 ) ;  
  4.      list.add(new  Integer (seatId) ); //典型的inner class 的应用,list为外部类的变量。    
  5.    }  
  6.  };  
和上面的RowCallbackHandler类似。
 
  1. public   interface  ResultSetExtractor {    
  2.       Object extractData(ResultSet rs) throws  SQLException, DataAccessException;    
  3. }   

      下面是JdbcTemplate中提供的模板方法。该方法完成对数据库的查询:),看看和上面最初的代码有多少不同。这里除了取数据库连接没有之外,其它的操作都已经有了:),并且很健壮。
       这个execute()方法非常关键。
java 代码
 
  1. public  Object query(  
  2.             PreparedStatementCreator psc, final  PreparedStatementSetter pss,  final  ResultSetExtractor rse)  
  3.             throws  DataAccessException {  
  4.   
  5.         Assert.notNull(rse, "ResultSetExtractor must not be null" );  
  6.   
  7.         if  (logger.isDebugEnabled()) {  
  8.             String sql = getSql(psc); //取得不变的SQL部分。 
  9.             logger.debug("Executing SQL query"  + (sql !=  null  ?  " ["  + sql  +  "]"  :  "" ));  
  10.         }  
  11.         return  execute(psc,  new  PreparedStatementCallback() {  
  12.             public  Object doInPreparedStatement(PreparedStatement ps)  throws  SQLException {  
  13.                 ResultSet rs = null ;  
  14.                 try  {  
  15.                     if  (pss !=  null ) {  
  16.                         pss.setValues(ps); / /就是给ps来设置参数用的。ps.setInt(1, 0);  
  17.                     }  
  18.   
  19.                     rs = ps.executeQuery(); //执行查询   
  20.   
  21.                     ResultSet rsToUse = rs;  
  22.                     if  (nativeJdbcExtractor !=  null ) {  
  23.                         rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);  
  24.                     }  
  25.                     return  rse.extractData(rsToUse);   // ResultSetExtractor从ResultSet中将值取出来就OK了。   
  26.   
  27.                 }  
  28.                 finally  {  
  29.                     //最后的善后工作还是需要做好的:) rs.close(),把ps的相关参数清除掉。   
  30.                     JdbcUtils.closeResultSet(rs);   
  31.                     if  (pss  instanceof  ParameterDisposer) {  
  32.                         ((ParameterDisposer) pss).cleanupParameters();  
  33.                     }  
  34.                 }  
  35.             }  
  36.         });  
  37.     }  


Are you ready?看看execute()方法吧。
java 代码
 
  1. public  Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)  
  2.             throws  DataAccessException {  
  3.   
  4.         Assert.notNull(psc, "PreparedStatementCreator must not be null" );  
  5.         Assert.notNull(action, "Callback object must not be null" );  
  6.   
  7.         //取得数据库的连接   
  8.         Connection con = DataSourceUtils.getConnection(getDataSource());  
  9.         PreparedStatement ps = null ;  
  10.         try  {  
  11.             Connection conToUse = con;  
  12.             if  ( this .nativeJdbcExtractor !=  null  &&  
  13.                     this .nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {  
  14.                 conToUse = this .nativeJdbcExtractor.getNativeConnection(con);  
  15.             }  
  16.             //创建PreparedStatement   
  17.             ps = psc.createPreparedStatement(conToUse);  
  18.   
  19.             applyStatementSettings(ps); //这个方法是设置ps的一些属性,我平时不用,Spring框架倒是考虑得相当全的说。   
  20.               
  21.             PreparedStatement psToUse = ps;  
  22.             if  ( this .nativeJdbcExtractor !=  null ) {  
  23.                 psToUse = this .nativeJdbcExtractor.getNativePreparedStatement(ps);  
  24.             }  
  25.             //调用Callback来完成PreparedStatement的设值。就是调用上面的 doInPreparedStatement来使用ps。  
  26.             Object result = action.doInPreparedStatement(psToUse);  
  27.            
  28.             SQLWarning warning = ps.getWarnings();  
  29.             throwExceptionOnWarningIfNotIgnoringWarnings(warning);  
  30.             return  result;  
  31.         }  
  32.         //如果有错误的话,那么就开始ps.close(), connection.close();   
  33.         catch  (SQLException ex) {  
  34.             // Release Connection early, to avoid potential connection pool deadlock   
  35.             // in the case when the exception translator hasn't been initialized yet.   
  36.             if  (psc  instanceof  ParameterDisposer) {  
  37.                 ((ParameterDisposer) psc).cleanupParameters();  
  38.             }  
  39.             String sql = getSql(psc);  
  40.             psc = null ;  
  41.             JdbcUtils.closeStatement(ps);   //就是ps.close();
  42.             ps = null ;  
  43.             DataSourceUtils.releaseConnection(con, getDataSource()); /  
  44.             con = null ;  
  45.             throw  getExceptionTranslator().translate( "PreparedStatementCallback" , sql, ex);  
  46.         }  
  47.         // 不管怎么样,ps.close(), Connection.close()吧,当然这里是releaseConnection。在我的程序 中,Connection只有一个,没有ConnectionPool,当然不会去close Connection。一般来讲,如果没有 Connection的线程池的话,我们肯定也不会经常的关闭Connection,得到Connection。毕竟这个东西非常耗费资源。   
  48.         finally  {  
  49.             if  (psc  instanceof  ParameterDisposer) {  
  50.                 ((ParameterDisposer) psc).cleanupParameters();  
  51.             }  
  52.             JdbcUtils.closeStatement(ps);  
  53.             DataSourceUtils.releaseConnection(con, getDataSource());   
  54.         }  
  55.     }  

JdbcTemplate完成了负责的操作,客户只需要调用query()就可以完成查询操作了。当然,JdbcTemplate会实现很多带其它参数的方法,以方便你的使用。Template设计模式被发扬广大了,我自己的程序中也主要是利用了Template。

       继续看看DataSourceUtils:这个专门用于管理数据库Connection的类。
   
java 代码
 
  1. public   static  Connection getConnection(DataSource dataSource)  throws  CannotGetJdbcConnectionException {  
  2.         try  {  
  3.             return  doGetConnection(dataSource);   
  4.                 ~~~~~~ //这个方法很舒服,Spring Framework中到处有这样的方法。为什么要委派到这个动作方法?  
  5.         }  
  6.         catch  (SQLException ex) {  
  7.             throw   new  CannotGetJdbcConnectionException( "Could not get JDBC Connection" , ex);  
  8.         }  
  9.     }  

    这里的doGetConnection就稍微复杂一点了。但是如果没有事务同步管理器的话,那就比较简单。
只是在Connection上多了一个ConnecionHolder类用于持有Connection,实现ConnectionPool的一点小功能。
      
java 代码
 
  1. public   static  Connection doGetConnection(DataSource dataSource)  throws  SQLException {  
  2.         Assert.notNull(dataSource, "No DataSource specified" );  
  3.   
  4.         ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
  5.         ~~~~~//Connection的持有器。通过持有器得到Connection。
  6.         if  (conHolder !=  null  && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {  
  7.             conHolder.requested();  
  8.             if  (!conHolder.hasConnection()) {  
  9.                 logger.debug("Fetching resumed JDBC Connection from DataSource" );  
  10.                 conHolder.setConnection(dataSource.getConnection());  
  11.             }  
  12.             return  conHolder.getConnection();   
  13.         }  
  14.         // Else we either got no holder or an empty thread-bound holder here.   
  15.   
  16.         logger.debug("Fetching JDBC Connection from DataSource" );  
  17.         Connection con = dataSource.getConnection();   
  18.         ……
  19.         return  con;  
  20.     }  

ConnectionHolder:Connection的持有器。通过ConnectionHandler来完成对Connection的操作:) 典型的委派。
 
  1. public   class  ConnectionHolder  extends  ResourceHolderSupport {  
  2.       
  3.     private  Connection currentConnection;  //当前的Connection   
  4.     private  ConnectionHandle connectionHandle;    //Connection的处理器,因此可以通过该类完成对connection的管理。   
  5.   
  6.     public  ConnectionHolder(Connection connection) {  
  7.         this .connectionHandle =  new  SimpleConnectionHandle(connection);  
  8.     }  
  9.       
  10.     public  ConnectionHolder(ConnectionHandle connectionHandle) {  
  11.         Assert.notNull(connectionHandle, "ConnectionHandle must not be null" );  
  12.         this .connectionHandle = connectionHandle;  
  13.     }  
  14.   
  15.     public  Connection getConnection() {  
  16.         Assert.notNull(this .connectionHandle,  "Active Connection is required" );  
  17.         if  ( this .currentConnection ==  null ) {  
  18.             this .currentConnection =  this .connectionHandle.getConnection();  
  19.         }  
  20.         return   this .currentConnection;  
  21.     }  
  22.   
  23.     public   void  released() {  
  24.         super .released();  
  25.         if  ( this .currentConnection !=  null ) {  
  26.             this .connectionHandle.releaseConnection( this .currentConnection);  
  27.             this .currentConnection =  null ;  
  28.         }  
  29.     }  

connectionHandle 的接口太纯粹了。但是我觉得这个设计太过于细致了:)
 
  1. public   interface  ConnectionHandle {  
  2.   
  3.     /**  
  4.      * Fetch the JDBC Connection that this handle refers to.  
  5.      */   
  6.     Connection getConnection();  
  7.   
  8.     /**  
  9.      * Release the JDBC Connection that this handle refers to.  
  10.      * @param con the JDBC Connection to release  
  11.      */   
  12.     void  releaseConnection(Connection con);  
  13.   
  14. }  

  最后看一下SimpleConnectionHandle,这个ConnectionHandle的简单实现类。就只有一个Connection可管理。如果有多个Connection可管理的话,这里就是ConnectionPool了:)

   
java 代码
 
  1. public   class  SimpleConnectionHandle  implements  ConnectionHandle {  
  2.   
  3.     private   final  Connection connection;  
  4.   
  5.   
  6.     /**  
  7.      * Create a new SimpleConnectionHandle for the given Connection.  
  8.      * @param connection the JDBC Connection  
  9.      */   
  10.     public  SimpleConnectionHandle(Connection connection) {  
  11.         Assert.notNull(connection, "Connection must not be null" );  
  12.         this .connection = connection;  
  13.     }  
  14.   
  15.     /**  
  16.      * Return the specified Connection as-is.  
  17.      */   
  18.     public  Connection getConnection() {  
  19.         return  connection;  
  20.     }  
  21.   
  22.     /**  
  23.      * This implementation is empty, as we're using a standard  
  24.      * Connection handle that does not have to be released.  
  25.      */   
  26.     public   void  releaseConnection(Connection con) {  
  27.     }  
  28.   
  29.   
  30.     public  String toString() {  
  31.         return   "SimpleConnectionHandle: "  +  this .connection;  
  32.     }  
  33.   
  34. }  

一路下来,真是很爽。Spring JDBC Framework真的可谓是深耕细作,这里只是管中窥豹了。类的职责设计得非常清除,同时有良好的设计模式支持,同时提供良好的编程接口,用户基本上只需要了结JdbcTemplate的API就可以了。厉害,厉害。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值