《Spring3實戰》摘要(5-1)征服數據庫

第5章 征服數據庫

5.1 Spring 的數據訪問哲學

DAO是數據訪問對象(data access object)的縮寫。DAO提供了數據讀取和寫入DAO數據庫中的一種方式。它們應該以接口的方式發佈功能,而應用程序的其他部分就可以通過接口來進行訪問了。

5.1.1 瞭解 Spring 的數據訪問異常體系

JDBC 的異常體系過於簡單,實際上,它算不上一個體系。而 Hibernate 的異常體系是其本身所獨有的。我們需要的是,數據訪問異常要具有描述性而且又與特定的持久化框架無關。

Spring 的平臺無關持久化異常

Spring JDBC 提供了多個數據訪問異常,分別描述了它們拋出時對應的問題。而且 Spring 的異常體系並沒有與特定的持久化方式相關聯。這意味着我們可以使用 Spring 拋出一致的異常,而不用關心所選擇的持久化方案。這有助於我們將所選擇持久化機制與數據訪問層隔離開來。

看!不用寫 catch 代碼塊

Spring 的數據訪問異常都繼承自 DataAccessException 。DataAccessException 的特殊之處在於它是一個非檢查型異常。換句話說,沒有必要捕獲 Spring 所拋出的數據訪問異常。

Spring 認爲觸發異常的很多問題是不能再 catch 代碼塊中修復。Spring 使用了費檢查型異常,而不是強制開發人員編寫 catch 代碼塊。這把是否要捕獲異常的權利留給了開發人員。

5.1.2 數據訪問模板化

模板方法模式:模板方法定義了過程的主要框架。從軟件術語來講,模板方法將過程中與特定實現相關的部分委託給接口,而這個接口的不同實現定義了過程中的具體行爲。這也是 Spring 再數據訪問中所使用的模式。

Spring 將數據訪問過程中固定的和可變的部分明確劃分爲兩個不同的類:模板(template)和回調(callback)模板管理過程中固定的部分,而回調處理自定義的數據訪問代碼。

針對不同的持久化平臺,Spring 提供了多個可選的模板。如果直接使用 JDBC ,則可以選擇 JdbcTemplate 。如果希望使用對象關係映射框架,則 HibernateTemplate 或 JpaTemplate 可能更適合你。

Spring 提供的數據訪問模板,分別使用與不同的持久化機制:

模板類(org.springframework.*) 用途
jca.cii.core.CciTemplate JCA CCI 連接
jdbc.core.JdbcTemplate JDBC 連接
jdbc.core.namedparam.NamedParameterJdbcTemplate 支持命名參數的 JDBC 連接
jdbc.core.simple.SimpleJdbcTemplate 通過 Java 5 簡化後的 JDBC 連接
orm.hibernate.HibernateTemplate Hibernate 2.x 的 Session
orm.hibernate3.HibernateTemplate Hibernate 3.x 的 Session
orm.ibatis.SqlMapClientTemplate iBATIS SqlMap 客戶端
orm.jdo.JdoTemplate Java 數據對象(Java Data Object)實現
orm.jpa.JpaTempalte Java 持久化 API 的實體管理器

5.1.3 使用 DAO 支持類

基於模板-回調設計,Spring 提供了 DAO 支持類,而將業務自己的 DAO 類作爲它的子類。

模板類、DAO支持類以及自定義 DAO 實現之間的關係,如下圖所示:
這裏寫圖片描述

當編寫應用程序自己的 DAO 實現時,可以繼承自 DAO 支持類並調用模板獲取方法 getXXXXTemplate() 來直接訪問底層的數據訪問模板。
另外,如果需要訪問底層的持久化平臺,則每個 DAO 支持類都能夠訪問其與數據庫進行通信的類。例如,JdbcDaoSupport 包含了一個 getConnection() 方法,可以用於直接處理 JDBC 連接。

5.2 配置數據源

Spring 提供了在 Spring 上下文中配置數據源 Bean 的多種方式,包括:

  • 通過 JDBC 驅動程序定義的數據源
  • 通過 JNDI 查找的數據源
  • 連接池的數據源

對於即將發佈到生產環境中的應用程序,我建議使用從連接池獲取連接的數據源。如果可能的話,我傾向於通過應用服務器的 JNDI 來獲取池中的數據源。

5.2.1 使用 JNDI 數據源

JNDI是 Java 命名與目錄接口(Java Naming and Directory Interface),在J2EE規範中是重要的規範之一。

JNDI 的用處:程序員應該不需要關心“具體的數據庫後臺是什麼? JDBC 驅動程序是什麼?JDBC URL格式是什麼?訪問數據庫的用戶名和口令是什麼?”等等這些問題,程序員編寫的程序應該沒有對 JDBC 驅動程序的引用,沒有服務器名稱,沒有用戶名稱或口令 —— 甚至沒有數據庫池或連接管理。而是把這些問題交給 J2EE 容器來配置和管理,程序員只需要對這些配置和管理進行引用即可。

Spring 應用程序經常部署在 Java EE 應用服務器中,如 WebSphere,JBoss 或者像 Tomcat 這樣的 Web 容器。這些服務器運行你配置通過 JNDI 獲取數據源。這種配置的好處在於數據源完全可以在應用程序之外進行管理,這樣應用程序只需在訪問數據庫的時候查找數據源就可以了。

利用 Spring ,我們可以像使用 Spring Bean 那樣配置 JNDI 中數據源的引用並將其裝配到需要的類中。位於 jee 命名空間下的 <jee:jndi-lookup> 元素可以用於檢索 JNDI 中的任何對象(包括數據源)並將其用於 Spring Bean 中。

示例如下:

<jee:jndi-lookup id="dataSource" jndi-name="/jdbc/SpitterDS" resource-ref="true" />

jndi-name 屬性用於指定 JNDI 中資源的名稱。如果只設置了 jndi-name 屬性,那麼就會根據指定的名稱查找數據源。但是,如果應用程序運行在 Java 應用程序服務器中,則需要將 resource-ref 屬性設置爲 true,這樣給定的 jndi-name 將會自動添加 java:comp/env/ 前綴。

5.2.2 使用數據源連接池

Spring 並沒有提供數據源連接池實現,但 Jakarta Commons Database Connection pooling (DBCP) 項目(http://jakarta.apache.org/commons/dbcp)是一個不錯的選擇。

DBCP 包含了多個提供連接池功能的數據源,其中 BasicDateSource 是最常用的,因爲它易於在 Spring 中配置,而且類似於 Spring 自帶的 DriveManagerSource (將在下一小節介紹)。

注:使用 dbcp 連接池需要添加 dbcp 的jar包。

<!-- 創建數據源,這裏使用 H2 數據庫-->
<!-- 前4個屬性是配置 BasicDataSource 所必需的。 -->
<bean id="dataSource" 
    class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:tcp://localhost/mem:spitter" />
    <property name="username" value="sa" />
    <property name="password" value="" />
    <!-- 連接池啓動時會創建5個連接,當需要更多連接時,允許最大的連接數爲10 -->
    <property name="initialSize" value="5" />
    <property name="maxActive" value="10" />
</bean>

配置 BasicDataSource 的其他常用屬性:

池配置屬性 所指定的內容
initalSize 池啓動時創建的連接數量
maxActive 同一時間可從池中分配的最多連接數,如果設置爲0,表示無限制
maxIdle 池裏不會被釋放的最多空間連接數,如果設置爲0,表示無限制
maxopenPreparedStatements 在同一時間能夠從語句池中分配的預處理語句的最大數量,如果設置爲0,表示無限制
maxWait 在拋出異常之前,池等待連接回收的最大時間(當沒有可用連接時),如果設置爲-1.表示無限等待
minEvictableTimeMillis 連接在池中保持空閒而不被回收的最大時間
minIdle 在不創建新連接的情況下,池中保持空閒的最小連接數
poolPreparedStatements 是否對預處理語句進行池管理(布爾值)

5.2.3 基於 JDBC 驅動的數據源

在 Spring 中,通過 JDBC 驅動定義數據源是最簡單的配置方式。Spring 提供了兩種數據源對象(均位於 org.springframework.jdbc.datasource 包中)供選擇。

  • DriverManagerDataSource:在每個連接請求時都會返回一個新建的連接。與 DBCP 的 BasicDataSource 不同,由 DriverManagerDataSource 提供的連接並沒有進行池化管理。
  • SingleConnectionSource:在每個連接請求時都會返回同一個連接。儘管 SingleConnectionDataSource 不是嚴格意義上的連接池數據源,但是你可以將其視爲只有一個連接的池。

以上兩個數據源的配置與 BasicDataSource 的配置類似,唯一的區別在於它們都沒有提供連接池功能,所以沒有可配置的池相關的屬性。

5.3 在 Spring 中使用 JDBC

5.3.1 應對失控的 JDBC 代碼

JDBC 的優點

  • 建立在標準 SQL 之上,不需要掌握其他框架的查詢語句;
  • JDBC 允許用戶使用數據庫的所有屬性,這是其他框架不鼓勵甚至禁止的;
  • JDBC 能夠更好地對數據訪問的性能進行調優;
  • 相對於持久層框架,JDBC 能在更低的層次上處理數據,能夠訪問和管理數據庫中單獨的列。

JDBC 的缺點

  • 爲了完成一項基本的數據庫操作,JDBC 需要實現大量用於創建連接和語句以及異常處理的樣板代碼。

5.3.2 使用 JDBC 模板

注:需要添加spring-jdbc.jar

Spring 的 JDBC 框架承擔了資源管理和異常處理的工作,從而簡化了 JDBC 代碼,讓我們只需要編寫從數據庫讀寫數據的必需代碼。

Spring 將數據訪問的樣板式代碼提取到模板類中。Spring爲 JDBC 提供了 3個模板類供使用。

  • org.springframework.jdbc.core.JdbcTemplate:最基本的 Spring JDBC 模板,這個模板支持最簡單的 JDBC 數據庫訪問功能以及簡單的索引參數查詢。
  • org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate:使用該模板類執行查詢時,可以將查詢值以命名參數的形式綁定到 SQL 中,而不是使用簡單的索引參數。
  • org.springframework.jdbc.core.simple.SimpleJdbcTemplate:該模板類利用 Java 5 的一些特性,如自動裝箱、泛型以及可變參數列表來簡化 JDBC 模板的使用。

注:在 eclipse 中實現書中例子時發現 SimpleJdbcTemplate 已經被標註爲@deprecated(聲明不贊成, 反對),查找網上說法是
“2.5的時候simplejdbctemplate把JdbcTemplate替代了,到了3.1 JdbcTemplate和NamedParameterJdbcTemplate又把simplejdbctemplate替代了

3者的區別可查看:http://www.cnblogs.com/xuyuanjia/p/5706372.html

5.3.2.1 JdbcTemplate

最基本的 Spring JDBC 模板,這個模板支持最簡單的 JDBC 數據庫訪問功能以及簡單的索引參數查詢。

public class JdbcSpitterDAO {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private static final String SQL_INSERT_SPITTER = "insert into spitter(username, password, email)"
            +" values(?, ?, ?)";

    public void addSpitter(Spitter spitter) {
        jdbcTemplate.update(SQL_INSERT_SPITTER,
                spitter.getUsername(),
                spitter.getPassword(),
                spitter.getEmail());
    }

}

5.3.2.2 NamedParameterJdbcTemplate

使用該模板類執行查詢時,可以將查詢值以命名參數的形式綁定到 SQL 中,而不是使用簡單的索引參數。

public class JdbcSpitterDAO {

    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;

    private static final String SQL_INSERT_SPITTER = "insert into spitter(username, password, email)"
            +" values(:username, :password, :email)";

    public void addSpitter(Spitter spitter) {
        Map<String,Object> params = new HashMap<String,Object>();
        params.put("username", spitter.getUsername());
        params.put("password", spitter.getPassword());
        params.put("email", spitter.getEmail());
        jdbcTemplate.update(SQL_INSERT_SPITTER,params);
    }
}

5.3.2.3 SimpleJdbcTemplate

該模板類利用 Java 5 的一些特性,如自動裝箱、泛型以及可變參數列表來簡化 JDBC 模板的使用。

SimpleJdbcTemplate 的 queryForObject() 方法來從數據庫查詢指定類型的Object。queryForObject() 方法有3個參數:

  • String,查找數據的 SQL
  • ParameterizedRowMapper 對象,用來從 ResultSet 中提取值並構建域對象;
  • 可變參數列表,列出了要綁定到查詢上的索引參數值。
public class JdbcSpitterDAO {
    @Autowired
    private SimpleJdbcTemplate jdbcTemplate;

    private static final String SQL_SELECT_SPITTER = "select id, username, password, email from spitter where id = ?";

    public Spitter getSpitterById(long id) {
        return jdbcTemplate.queryForObject(SQL_SELECT_SPITTER,
                new ParameterizedRowMapper<Spitter>(){
                    @Override
                    public Spitter mapRow(ResultSet rs, int rowNum) throws SQLException {
                        Spitter spitter = new Spitter();
                        spitter.setId(rs.getLong(1));
                        spitter.setUsername(rs.getString(2));
                        spitter.setPassword(rs.getString(3));
                        spitter.setEmail(rs.getString(4));
                        return spitter;
                    }
                },
                id);
    }
}

使用 JDBC 模板訪問數據

只需要設置 DataSource 就能夠讓 NamedParameterJdbcTemplate 正常工作:

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    <!-- 屬性 dataSource 可以是 javax.sql.DataSource 的任意實現 -->
    <constructor-arg ref="dataSource" />
</bean>

將 jdbcTemplate 裝配到 DAO 中並使用它來訪問數據庫。

public class JdbcSpitterDAO implements SpitterDAO{

    //....
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    //....
}

使用 Spring 的 JDBC DAO 支持類
對於應用程序中的多個 JDBC DAO 類,如果我們都需要添加 jdbcTemplate 屬性以及對應的 setter 方法,這就會產生大量的重複性工作。

一種可行的解決方案是爲所有的 DAO 創建一個通用的父類,在區中會有 jdbcTemplate屬性。

Spring 提供了內置的基類來設置 JDBC 模板(JdbcDaoSupport、SimpleJdbcDaoSupport 和 NamedParameterJdbcDaoSupport)。子類 DAO 類 繼承相應基類後使用 getXxxTemplate() 方法獲得 JDBC Template 對象。

另一種方法是,直接將數據源配置給自定義的 DAO 類,該自定義類需繼承 Spring 提供的 JDBC DAO 支持類。這樣就不需要在 Spring 配置文件中明確聲明 XxxTemplate Bean 了。

發佈了66 篇原創文章 · 獲贊 37 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章