Spring JDBC最佳實踐
Spring提供了兩種使用JDBC API的最佳實踐,一種是以JdbcTemplate為核心的基于Template的JDBC的使用方式,另一種則是在JdbcTemplate基礎之上的構建的基于操作對象的JDBC的使用方式。
基于Template的JDBC的使用方式
該使用方式的最初設想和原型,需要追溯到Rod Johnson在03年出版的Expert One-on-One J2EE Design and Development,在該書的Practical Data Access(數據訪問實踐)中,Rod針對JDBC使用中的一些問題提出了一套改進的實踐原型,并最終將該原型完善后在Spring框架中發布。
JDBC的尷尬
JDBC作為Java平臺的訪問關系數據庫的標準,其成功是 有目共睹的。幾乎所有java平臺的數據訪問,都直接或者間接的使用了JDBC,它是整個java平臺面向關系數據庫進行數據訪問的基石。
作為一個標準,無疑JDBC是很成功的,但是要說JDBC在使用過程當中多么的受人歡迎,則不盡然了。JDBC主要是面向較為底層的數據庫操作,所以在設計的過程當中 ,比較的貼切底層以提供盡可能多的功能特色。從這個角度來說,JDBC API的設計無可厚非。可是,過于貼切底層的API的設計,對于開發人員則未必是一件好事。即使執行一個最簡單的查詢,開發人員也要按照API的規矩寫上一大堆雷同的代碼,如果不能合理的封裝使用JDBC API,在項目中使用JDBC訪問數據所出現的問題估計會使人發瘋!
對于通常的項目開發來說,如果層次劃分很明確,數據訪問邏輯一般應該在DAO層中實現。根據功能模塊的劃分,可能每個開發人員都會分得或多或少的實現相應的DAO的任務,假設開發人員A在分得了DAO實現任務后進行開發,他或許開發了如下所示的代碼:
package com.google.spring.jdbc; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class DaoWithA implements IDao { private final Log logger = LogFactory.getLog(DaoWithA.class); private DataSource dataSource = null; public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } @Override public int updateSomething(String sql) { int count; Connection conn = null; Statement stmt = null; try { conn = getDataSource().getConnection(); stmt = conn.createStatement(); count = stmt.executeUpdate(sql); stmt.close(); stmt = null; } catch (SQLException e) { throw new RuntimeException(e); } finally { if(stmt!=null) { try { stmt.close(); } catch (SQLException ex) { logger.warn("fail to close statement:"+ex); } } if(conn!=null) { try { conn.close(); } catch (Exception ex) { logger.warn("failed to close Connection:"+ex); } } } return count; } }而B所負責的DAO的實現中,可能也有類似的更新的操作。無疑,B也要像A這樣,在他的DAO實現類中寫一大堆同樣的JDBC代碼,類似的情況還可能擴展到C、D等開發人員。如果每個開發人員都能嚴格的按照JDBC的編程規范開發還好,但是事實是,一個團隊中的開發人員是有差別的。
這其實只是API的使用過程中的一個插曲,當你看到應用程序中成百的使用JDBC實現類的時候,會發現如下的問題:
1、Statement使用完沒有關閉,而是想著讓Connection關閉的時候一并關閉,可是并非所有的驅動程序都有這樣的行為。
2、創建了多個ResultSet或者Statement,只清理了最外層的,忽視了里層的。
3、忘記關閉Connection。
JDBC規范在指定數據庫訪問異常的時候也沒有能夠進行的很徹底:
1、將異常類型定義為SQLException是一個值得商榷的地方。
2、SQLExcpetion沒有采用將具體的異常情況子類化,以進一步抽象不同的數據訪問的情況,而是采用ErrorCode的方式來區分訪問過程中所出現的不同異常情況,其實這也沒什么,只要能區分出具體的錯誤就行,但是JDBC規范卻把ErrorCode的規范留給了數據庫提供商,這導致了不同的數據庫供應商對應了不同的ErrorCode,進而應用程序在捕獲到SQLException后,還要看當前用的是什么數據庫。
針對以上問題,Spring提供了相應的解決方案幫助我們提高開發效率!