第一章
Spring的數據庫支持
5.1 Spring對DAO的支持
Spring提供的DAO(數據訪問對象)支持主要的目的是便於以標準的方式使用不一樣的數據訪問技術, 如JDBC,Hibernate或者JDO等。它不只可讓你方便地在這些持久化技術間切換, 並且讓你在編碼的時候不用考慮處理各類技術中特定的異常。
1) 一致的異常層次
Spring提供了一種方便的方法,把特定於某種技術的異常,如SQLException, 轉化爲本身的異常,這種異常屬於以DataAccessException 爲根的異常層次。這些異常封裝了原始異常對象,這樣就不會有丟失任何錯誤信息的風險。
除了對JDBC異常的封裝,Spring也對Hibernate異常進行了封裝,把它們從一種專有的checked異常 (Hibernate3.0之前的版本),轉化爲一系列抽象的運行時異常。對JDO也是這樣。 它可讓你輕鬆處理大多數持久化異常(這些異常大可能是不可恢復的,並且只出如今特定 的層次),而再也不須要討厭的樣板式catch/throw代碼塊和異常聲明。你仍然能夠在須要 的地方捕獲並處理這些異常。就像咱們上面提到的,JDBC異常(包括特定於某種數據庫 方言的異常)也能夠被轉化爲一樣的異常層次,這意味着你能夠在一致的編程模型下,通 過JDBC來執行某些操做。
上述狀況適用於各類使用模板方式的ORM訪問框架。若是使用攔截器方式,你在應用中 就得本身當心處理HibernateException、 JDOException等,最好是委託給 SessionFactoryUtils的 convertHibernateAccessException、 convertJdoAccessException等方法。這些方法能夠把相應的異常轉 化爲與org.springframework.dao中定義的異常層次相兼容的異常。其中JDOException屬unchecked異常,它們則被簡單地拋出,儘管這在異常處理方面犧牲了通用的DAO抽象。
2) 一致的DAO支持抽象類
爲了便於以一種一致的方式使用各類數據訪問技術,如JDBC、JDO和Hibernate, Spring提供了一套抽象DAO類供你擴展。這些抽象類提供了一些方法,經過它們你能夠 得到與你當前使用的數據訪問技術相關的數據源和其餘配置信息。
Dao支持類:
JdbcDaoSupport - JDBC數據訪問對象的基類。 須要一個DataSource,同時爲子類提供 JdbcTemplate。
HibernateDaoSupport - Hibernate數據訪問對象的基類。 須要一個SessionFactory,同時爲子類提供 HibernateTemplate。也能夠選擇直接經過 提供一個HibernateTemplate來初始化, 這樣就能夠重用後者的設置,例如SessionFactory, flush模式,異常翻譯器(exception translator)等等。
5.2 在Spring中使用JDBC
Java開發人員都有過直接使用JDBC編寫數據庫程序的經歷,因爲JDBC API過於底層,開發人員不但須要編寫數據操做代碼,還須要編寫獲取JDBC連接、處理異常、釋放資源等代碼。即便是一個再簡單不孤傲的數據庫操做,也須要至少幾十行的代碼。Spring JDBC經過模板和回調機制大大下降了使用JDBC的複雜度,藉由JdbcTemplate的幫助,咱們僅須要編寫那些「必不可少」的代碼就能夠進行數據庫操做了。
1)簡單的
JdbcTemplate
代碼清單1
import
org.springframework.jdbc.core.JdbcTemplate;
import
org.springframework.jdbc.datasource.DriverManagerDataSource;
public
class
JdbcDAO {
public
static
void
main(String[] args) {
DriverManagerDataSource ds =
new
DriverManagerDataSource();
ds.setDriverClassName(
"net.sourceforge.jtds.jdbc.Driver"
);
ds.setUrl(
"jdbc:jtds:Sqlserver://10.0.160.188:1433/WPE"
);
ds.setUsername(
"sa"
);
ds.setPassword(
"sa"
);
JdbcTemplate jdbcTemplate =
new
JdbcTemplate();
jdbcTemplate.setDataSource(ds);
String sql =
"create table T_USER(U_ID int primary key ,U_NAME varchar(20))"
;
jdbcTemplate.execute(sql);
}
}
代碼清單
1
中咱們建立了一個
DriverManagerDataSource
數據源對象,又建立了一個
JdbcTemplate
對象經過調用
JdbcTemplate
對象的
execute
方法向數據庫中建立一個表。很簡單的一個例子可是咱們已經感覺到
JdbcTemplate
的力量。
2)
使用
JdbcDaoSupport
咱們看到代碼清單中的
JdbcTemplate
訪問數據庫是那麼的簡單,可是
Spring
還進一步簡化了模板類的支持類,
JdbcDaoSupport
自己包含了一個
JdbcTemplate
類實例變量,並開放了設置
dataSource
的接口,這樣咱們僅須要簡單地擴展
JdbcDaoSupport
就能夠定以本身的
DAO
類了。
代碼清單
2
<!--
定義
dataSource -->
<
bean
id
=
"dataSource"
class
=
"net.sourceforge.jtds.jdbc.Driver"
>
<
property
name
=
"driverClassName"
value
=
"net.sourceforge.jtds.jdbc.Driver"
/>
<
property
name
=
"url"
value
=
"jdbc:jtds:Sqlserver://10.0.160.188:1433/WPE"
/>
<
property
name
=
"username"
value
=
"sa"
/>
<
property
name
=
"password"
value
=
"sa"
/>
</
bean
>
<!--
定義
jdbcTemplate -->
<
bean
id
=
"jdbcTemplate"
class
=
"org.springframework.jdbc.core.JdbcTemplate"
>
<
property
name
=
"dateSource"
ref
=
"dataSource"
/>
</
bean
>
<!--
定義
dao
抽象父類用於其它類使用
-->
<
bean
id
=
"dao"
abstract
=
"true"
>
<
property
name
=
"jdbcTemplate"
ref
=
"jdbcTemplate"
/>
</
bean
>
<
bean
id
=
"jdbcDAO"
class
=
"com.tony.test.JdbcDAO"
parent
=
"dao"
/>
import
org.springframework.jdbc.core.support.JdbcDaoSupport;
public
class
JdbcDAO
extends
JdbcDaoSupport{
public
void
save(String name) {
//
向數據庫中插入一條記錄
String sql =
"INSERT INTO T_USER(U_NAME) VALUES(?)"
;
this
.getJdbcTemplate()
.update(sql,
new
Object[]{name});
}
}
代碼清單
2
中咱們經過
Spring
的配置文件將
JdbcDAO
裝配完成
,
咱們就能夠經過
this
.getJdbcTemplate()
得到
JdbcTemplate
對象直接操做數據庫。除了標準的
JdbcTemplate
之外,在
Spring2.0
中新增了
NamedParameterJdbcDaoSupport
以提供明明參數綁定的功能。
3)NamedParameterJdbcDaoSupport
在低版本的
Spring
中
,
用戶只能使用
?
佔位符聲明參數,並使用索引號綁定參數,使用這種方法綁定參數時,必須足夠當心,以保證參數的索引號和
SQL
語句中佔位符(?)的位置正確匹配。這種編程模式被認爲是弱穩定的,由於當新增一個?佔位符時,可能致使原來全部的參數綁定方法都須要所以調整索引號,這極有可能引入一些不容易發現的錯誤。
Spring2.0
提供了新的支持命名參數綁定的
NamedParameterJdbcDaoSupport
模板類,來一塊兒看看下面的代碼
代碼清單
3
import
org.springframework.jdbc.core.namedparam.
NamedParameterJdbcDaoSupport;
public
class
JdbcDAO
extends
NamedParameterJdbcDaoSupport{
public
void
save(String name) {
Map<String,String> value =
new
HashMap<String,String>();
value.put(
"U_NAME"
, name);
//
定義了一個
Map,
將
name put
進
Map
String sql =
"INSERT INTO T_USER(U_NAME) VALUES(:U_NAME)"
;
//
調用
this
.getNamedParameterJdbcTemplate()
對數據庫操做
this
.getNamedParameterJdbcTemplate().update(sql,value);
}
}
在代碼清單中咱們看到使用
NamedParameterJdbcTemplate
的方法更加靈活,而且
SQL
語句也更加清晰。
5.3 Spring的ORM框架支持
Spring在資源管理,DAO實現支持以及事務策略等方面提供了與 Hibernate、JDO、Oracle TopLink、iBATIS SQL Mappings 以及 JPA 的集成。以Hibernate爲例,Spring經過使用許多IoC的便捷特性對它提供了一流的支持,幫助咱們處理不少典型的Hibernate整合的問題。全部的這些支持,都遵循Spring通用的事務和DAO異常體系。一般來講有兩種不一樣的整合風格:咱們可使用Spring提供的DAO模板,或者直接使用Hibernate/JDO/TopLink等工具的原生API編寫DAO。不管採起哪一種風格,這些DAO均可以經過IoC進行配置,並參與到Spring的資源和事務管理中去。
使用Spring構建O/R Mapping DAO的好處包括:
1)
測試簡單。 Spring的IoC使得替換不一樣的實現和配置變得很是簡單,這些內容包括:Hibernate SessionFactory
的位置,JDBC DataSource
,事務管理器以及映射對象的實現(若是須要)等。這樣也就很容易隔離並測試持久化相關的代碼的各個部分。
2)
異常封裝。 Spring可以封裝你所選擇的O/R Mapping工具所拋出的異常,將它們從專有的、潛在的checked exception轉化爲一組抽象的runtime DataAccessException體系。這可使你僅須要在恰當的應用程序層次去處理大部分不可恢復的持久層異常,從而避免了不少使人討厭的catch/throw以及異常聲明。固然,你仍是能夠在你須要的地方捕捉和處理異常。回想一下JDBC異常(包括與DB相關的Dialect)被轉變爲一樣的異常體系,這就意味着你能夠在一致的編程模型中處理JDBC操做。
3)
通用的資源管理。 Spring的application context可以處理諸如Hibernate的 SessionFactory
, JDBC的 DataSource
,iBatis的SQL Maps配置對象以及其餘相關資源的定位和配置。這樣,這些配置的值很容易被管理和修改。Spring提供了簡單、有效、安全的對持久層資源的處理。以Hibernate爲例,一般在使用Hibernate時,須要使用同一個Hibernate Session
對象以確保高效和恰當地事務處理。 Spring讓咱們可以很容易透明地建立並綁定一個 Session
到當前線程。你可使用如下兩種辦法之一:經過使用一個外部的template包裝類在Java代碼層次實現,或者經過Hibernate的 SessionFactory
暴露當前 Session
對象(對於那些創建在Hibernate3原生的API上的DAO)。這樣,對於任何的事務環境(本地事務或者JTA),Spring解決了許多在Hibernate使用中不斷出現的這樣那樣的問題。
4)
綜合的事務管理。 Spring容許你封裝你的O/R Mapping代碼,這能夠經過聲明式的AOP方法攔截器或者在Java代碼級別上使用一個外部的template包裝類。不管使用哪種方式,事務控制都會幫助你作相關處理,例如萬一有異常發生時的事務操做(rollback)。正如咱們下面要討論的同樣,你可以使用和替換各類事務管理器,卻不會使你的Hibernate/JDO相關的代碼受到影響。例如,無論採用本地事務仍是JTA,完整的Service層的代碼(如聲明式事務管理)在這種場景下都是相同的。做爲一個附加的功能,JDBC相關的代碼可以在事務級別上與你所使用的O/R映射代碼無縫整合。這一功能對於那些諸如批量處理、BLOB的操做等並不適合採用O/R Mapping操做的,可是須要與O/R Mapping操做一塊兒參與相同的事務來講是至關有用的。
5)
避免綁定特定技術容許mix-and-match的實現策略。 雖然Hibernate很是強大、靈活、開源並且免費,但它仍是使用了本身的特定的API。此外,有人也許會爭辯:iBatis更輕便並且在不須要複雜的O/R映射策略的應用中使用時可以表現得很是優秀。若是能夠選擇的話,使用標準或抽象的API來實現主要的應用需求一般是更好的,尤爲是當你可能會由於功能、性能或其餘方面的緣由而須要切換到另外一種實現的時候。舉例來講,Spring對Hibernate事務和異常抽象,容許你經過IoC機制輕鬆封裝mapper和DAO對象來實現數據訪問功能,這些特性都可以使你在 不犧牲Hibernate強大功能 的狀況下在你的應用程序中隔離Hibernate的相關代碼。處理DAO的高層次的service代碼無需知道DAO的具體實現。這一機制能夠很容易使用mix-and-match方案互不干擾地實現數據訪問層(好比在一些地方用Hibernate,一些地方使用JDBC,其餘地方使用iBatis), mix-and-match的特性也有利於處理遺留代碼並在各類技術(JDBC、Hibernate和iBatis)之間取長補短。
咱們將首先從Hibernate開始,經過講解Hibernate在Spring環境中的使用來闡述Spring框架對於O/R Mapping工具的整合方式。本章節將涉及到許多細節問題,並向你展現各類不一樣的DAO實現方式和事務劃分。這其中的絕大多數模式可以被Spring支持的其餘O/R Mapping工具所使用。爲了不硬編碼的資源查找與應用程序對象緊密耦合,Spring容許你在application context中以bean的方式定義諸如JDBC DataSource或者Hibernate SessionFactory
的數據訪問資源。任何須要進行資源訪問的應用程序對象只須要持有這些事先定義好的實例的引用,下面的代碼演示如何建立一個Hibernate SessionFactory
。
代碼清單1
<!--
定義
dataSource -->
<
bean
id
=
"dataSource"
class
=
"net.sourceforge.jtds.jdbc.Driver"
>
<
property
name
=
"driverClassName"
value
=
"net.sourceforge.jtds.jdbc.Driver"
/>
<
property
name
=
"url"
value
=
"jdbc:jtds:Sqlserver://10.0.160.188:1433/WPE"
/>
<
property
name
=
"username"
value
=
"sa"
/>
<
property
name
=
"password"
value
=
"sa"
/>
</
bean
>
<
bean
id
=
"mySessionFactory"
class
=
"org.springframework.orm.hibernate3.LocalSessionFactoryBean"
>
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
<
property
name
=
"mappingResources"
>
<
list
>
<
value
>
product.hbm.xml
</
value
>
</
list
>
</
property
>
<
property
name
=
"hibernateProperties"
>
<
value
>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
</
value
>
</
property
>
</
bean
>
public
class
HibernateDAO
extends
HibernateDaoSupport {
public
void
save(User uaer){
this
.getHibernateTemplate().save(user);
}
}
在代碼清單
1
中咱們定義了一個數據源而且經過這個數據源咱們建立了一個
Hibernate
的
SessionFactory
,經過
Spring
將
SessionFactory
注入進
HibernateDAO
,在
save
方法中調用
HibernateTemplate
的
save
方法將
User
對象持久化入數據庫。