The important thing in life is to have a great aim , and the determination

          常用鏈接

          統(tǒng)計

          IT技術(shù)鏈接

          保險相關(guān)

          友情鏈接

          基金知識

          生活相關(guān)

          最新評論

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

          下面我們看看Spring JDBC相關(guān)的實現(xiàn),
          在Spring中,JdbcTemplate是經(jīng)常被使用的類來幫助用戶程序操作數(shù)據(jù)庫,在JdbcTemplate為用戶程序提供了許多便利的數(shù)據(jù)庫操作方法,比如查詢,更新等,而且在Spring中,有許多類似 JdbcTemplate的模板,比如HibernateTemplate等等 - 看來這是Rod.Johnson的慣用手法,一般而言這種Template中都是通過回調(diào)函數(shù)CallBack類的使用來完成功能的,客戶需要在回調(diào)接口中實現(xiàn)自己需要的定制行為,比如使用客戶想要用的SQL語句等。不過往往Spring通過這種回調(diào)函數(shù)的實現(xiàn)已經(jīng)為我們提供了許多現(xiàn)成的方法供客戶使用。一般來說回調(diào)函數(shù)的用法采用匿名類的方式來實現(xiàn),比如:
          Java代碼 復(fù)制代碼 收藏代碼
          1. JdbcTemplate = new JdbcTemplate(datasource);   
          2. jdbcTemplate.execute(new CallBack(){   
          3.             public CallbackInterfacedoInAction(){   
          4.                ......   
          5.                //用戶定義的代碼或者說Spring替我們實現(xiàn)的代碼   
          6.             }   
          7. }  

          在模板中嵌入的是需要客戶化的代碼,由Spring來作或者需要客戶程序親自動手完成。下面讓我們具體看看在JdbcTemplate中的代碼是怎樣完成使命的,我們舉JdbcTemplate.execute()為例,這個方法是在JdbcTemplate中被其他方法調(diào)用的基本方法之一,客戶程序往往用這個方法來執(zhí)行基本的SQL語句:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public Object execute(ConnectionCallback action) throws DataAccessException {   
          2.     //這里得到數(shù)據(jù)庫聯(lián)接   
          3.     Connection con = DataSourceUtils.getConnection(getDataSource());   
          4.     try {   
          5.         Connection conToUse = con;   
          6.         //有些特殊的數(shù)據(jù)庫,需要我們使用特別的方法取得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.     //這里調(diào)用的是傳遞進(jìn)來的匿名類的方法,也就是用戶程序需要實現(xiàn)CallBack接口的地方。   
          16.         return action.doInConnection(conToUse);   
          17.     }   
          18.     catch (SQLException ex) {   
          19.         //如果捕捉到數(shù)據(jù)庫異常,把數(shù)據(jù)庫聯(lián)接釋放,同時拋出一個經(jīng)過Spring轉(zhuǎn)換過的Spring數(shù)據(jù)庫異常,   
          20.         //我們知道,Spring做了一個有意義的工作是把這些數(shù)據(jù)庫異常統(tǒng)一到自己的異常體系里了。   
          21.         DataSourceUtils.releaseConnection(con, getDataSource());   
          22.         con = null;   
          23.         throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);   
          24.     }   
          25.     finally {   
          26.         //最后不管怎樣都會把數(shù)據(jù)庫連接釋放   
          27.         DataSourceUtils.releaseConnection(con, getDataSource());   
          28.     }   
          29. }  

          對于JdbcTemplate中給出的其他方法,比如query,update,execute等的實現(xiàn),我們看看query():
          Java代碼 復(fù)制代碼 收藏代碼
          1. public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)   
          2.         throws DataAccessException {   
          3.     ..........   
          4.     //這里調(diào)用了我們上面看到的execute()基本方法,然而這里的回調(diào)實現(xiàn)是Spring為我們完成的查詢過程   
          5.     return execute(psc, new PreparedStatementCallback() {   
          6.         public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {   
          7.             //準(zhǔn)備查詢結(jié)果集   
          8.             ResultSet rs = null;   
          9.             try {   
          10.             //這里配置SQL參數(shù)   
          11.                 if (pss != null) {   
          12.                     pss.setValues(ps);   
          13.                 }   
          14.           //這里執(zhí)行的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.         //最后關(guān)閉查詢的紀(jì)錄集,對數(shù)據(jù)庫連接的釋放在execute()中釋放,就像我們在上面分析的看到那樣。   
          25.                 JdbcUtils.closeResultSet(rs);   
          26.                 if (pss instanceof ParameterDisposer) {   
          27.                     ((ParameterDisposer) pss).cleanupParameters();   
          28.                 }   
          29.             }   
          30.         }   
          31.     });   
          32. }  

          輔助類DataSourceUtils來用來對數(shù)據(jù)庫連接進(jìn)行管理的主要工具,比如打開和關(guān)閉數(shù)據(jù)庫連接等基本操作:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public static Connection doGetConnection(DataSource dataSource) throws SQLException {   
          2.    //把對數(shù)據(jù)庫連接放到事務(wù)管理里面進(jìn)行管理   
          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.     // 這里得到需要的數(shù)據(jù)庫連接,在配置文件中定義好的。   
          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. }  

          那我們實際的DataSource對象是怎樣得到的?很清楚我們需要在上下文中進(jìn)行配置:它作為JdbcTemplate父類JdbcAccessor的屬性存在:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public abstract class JdbcAccessor implements InitializingBean {   
          2.   
          3.     /** 這里是我們依賴注入數(shù)據(jù)庫數(shù)據(jù)源的地方。 */  
          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. }  

          而對于DataSource的緩沖池實現(xiàn),我們通過定義Apache Jakarta Commons DBCP或者C3P0提供的DataSource來完成,然后只要在上下文中配置好就可以使用了。從上面我們看到JdbcTemplate提供了許多簡單查詢和更新功能,但是如果需要更高層次的抽象,以及更面向?qū)ο蟮姆椒▉碓L問數(shù)據(jù)庫。Spring為我們提供了org.springframework.jdbc.object包,這里面包含了SqlQuery,SqlMappingQuery, SqlUpdate和StoredProcedure等類,這些類都是Spring JDBC應(yīng)用程序可以使用的主要類,但我們要注意使用這些類的時候,用戶需要為他們配置好一個JdbcTemplate作為其基本的操作的實現(xiàn)。
          比如說我們使用MappingSqlQuery來將表數(shù)據(jù)直接映射到一個對象集合 - 具體可以參考書中的例子
          1.我們需要建立DataSource和sql語句并建立持有這些對象的MappingSqlQuery對象
          2.然后我們需要定義傳遞的SqlParameter,具體的實現(xiàn)我們在MappingSqlQuery的父類RdbmsOperation中可以找到:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {   
          2.    //如果聲明已經(jīng)被編譯過,則該聲明無效   
          3.    if (isCompiled()) {   
          4.        throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled");   
          5.    }   
          6.    //這里對參數(shù)值進(jìn)行聲明定義   
          7.    this.declaredParameters.add(param);   

          而這個declareParameters維護(hù)的是一個列表:
          Java代碼 復(fù)制代碼 收藏代碼
          1. /** List of SqlParameter objects */  
          2. private List declaredParameters = new LinkedList();  

          這個列表在以后compile的過程中會被使用。
          3.然后用戶程序需要實現(xiàn)MappingSqlQuery的mapRow接口,將具體的ResultSet數(shù)據(jù)生成我們需要的對象,這是我們迭代使用的方法。1,2,3步實際上為我們定義好了一個迭代的基本單元作為操作模板。
          4.在應(yīng)用程序,我們直接調(diào)用execute()方法得到我們需要的對象列表,列表中的每一個對象的數(shù)據(jù)來自于執(zhí)行SQL語句得到記錄集的每一條記錄,事實上執(zhí)行的execute在父類SqlQuery中起作用:
          Java代碼 復(fù)制代碼 收藏代碼
          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來完成對數(shù)據(jù)庫的查詢操作,所以我們說JdbcTemplate是基本的操作類。   
          7.      return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);   
          8. }  

          在這里我們可以看到template模式的精彩應(yīng)用和對JdbcTemplate的靈活使用。通過使用它,我們免去了手工迭代ResultSet并將其中的數(shù)據(jù)轉(zhuǎn)化為對象列表的重復(fù)過程。在這里我們只需要定義SQL語句和SqlParameter - 如果需要的話,往往SQL語句就常常能夠滿足我們的要求了。這是靈活使用JdbcTemplate的一個很好的例子。
          Spring還為其他數(shù)據(jù)庫操作提供了許多服務(wù),比如使用SqlUpdate插入和更新數(shù)據(jù)庫,使用UpdatableSqlQuery更新ResultSet,生成主鍵,調(diào)用存儲過程等。
          書中還給出了對BLOB數(shù)據(jù)和CLOB數(shù)據(jù)進(jìn)行數(shù)據(jù)庫操作的例子:
          對BLOB數(shù)據(jù)的操作通過LobHander來完成,通過調(diào)用JdbcTemplate和RDBMS都可以進(jìn)行操作:
          在JdbcTemplate中,具體的調(diào)用可以參考書中的例子 - 是通過以下調(diào)用起作用的:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {   
          2.     return execute(new SimplePreparedStatementCreator(sql), action);   
          3. }  

          然后通過對實現(xiàn)PreparedStatementCallback接口的AbstractLobCreatingPreparedStatementCallback的回調(diào)函數(shù)來完成:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {   
          2.     LobCreator lobCreator = this.lobHandler.getLobCreator();   
          3.     try {   
          4.         //這是一個模板方法,具體需要由客戶程序?qū)崿F(xiàn)   
          5.         setValues(ps, lobCreator);   
          6.         return new Integer(ps.executeUpdate());   
          7.     }   
          8.     finally {   
          9.         lobCreator.close();   
          10.     }   
          11. }   
          12. //定義的需要客戶程序?qū)崿F(xiàn)的虛函數(shù)   
          13. protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)   
          14.         throws SQLException, DataAccessException;  

          而我們注意到setValues()是一個需要實現(xiàn)的抽象方法,應(yīng)用程序通過實現(xiàn)setValues來定義自己的操作 - 在setValues中調(diào)用lobCreator.setBlobAsBinaryStrem()。讓我們看看具體的BLOB操作在LobCreator是怎樣完成的,我們一般使用DefaultLobCreator作為BLOB操作的驅(qū)動:
          Java代碼 復(fù)制代碼 收藏代碼
          1. public void setBlobAsBinaryStream(   
          2.         PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength)   
          3.         throws SQLException {   
          4.     //通過JDBC來完成對BLOB數(shù)據(jù)的操作,對Oracle,Spring提供了OracleLobHandler來支持BLOB操作。   
          5.     ps.setBinaryStream(paramIndex, binaryStream, contentLength);   
          6.     ........   
          7. }  

          上面提到的是零零碎碎的Spring JDBC使用的例子,可以看到使用Spring JDBC可以幫助我們完成許多數(shù)據(jù)庫的操作。Spring對數(shù)據(jù)庫操作最基本的服務(wù)是通過JdbcTeamplate和他常用的回調(diào)函數(shù)來實現(xiàn)的,在此之上,又提供了許多RMDB的操作來幫助我們更便利的對數(shù)據(jù)庫的數(shù)據(jù)進(jìn)行操作 - 注意這里沒有引入向Hibernate這樣的O/R方案。對這些O/R方案的支持,Spring由其他包來完成服務(wù)。
          書中還提到關(guān)于execute和update方法之間的區(qū)別,update方法返回的是受影響的記錄數(shù)目的一個計數(shù),并且如果傳入?yún)?shù)的話,使用的是java.sql.PreparedStatement,而execute方法總是使用 java.sql.Statement,不接受參數(shù),而且他不返回受影響記錄的計數(shù),更適合于創(chuàng)建和丟棄表的語句,而update方法更適合于插入,更新和刪除操作,這也是我們在使用時需要注意的。

          posted on 2011-10-26 23:37 鴻雁 閱讀(341) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 甘谷县| 寿宁县| 筠连县| 濮阳县| 项城市| 远安县| 夏河县| 资阳市| 女性| 阳高县| 津市市| 赤水市| 石林| 包头市| 林州市| 阳高县| 屏东市| 绥德县| 洪雅县| 梁山县| 莆田市| 景宁| 六枝特区| 泸州市| 通化县| 璧山县| 桓台县| 灵武市| 莱西市| 芒康县| 望奎县| 福海县| 绥芬河市| 谷城县| 普安县| 定日县| 石家庄市| 安图县| 蓬莱市| 张家界市| 钟山县|