dao:資料通路對象(data access object)的縮寫。
可能導緻抛出sqlexception的常見問題包括:
1、應用程式無法連接配接資料庫。
2、要執行的查詢有文法錯誤。
3、查詢中所使用的表和(或)列不存在。
4、試圖插入或更新的資料違反了資料庫的完整性限制。
它不是對每種可能的問題都會有不通的異常類型。而都是抛出sqlexception。
spring的異常大多繼承自dataaccessexception。這個異常的特殊指出在于它是一個非檢查型異常。說白了不用寫try catch。這把是否捕獲異常的權利留給了開發人員。
spring提供的資料通路模版,分别适用于不通的持久化機制。
下面分别是模版類和它的用途:
jca.cci.core.ccitemplate : jca cci連接配接
jdbc.core.jdbctemplate : jdbc連接配接
jdbc.core.namedparam.namedparameterjdbctemplate : 支援命名參數的jdbc連接配接
jdbc.core.simple.simplejdbctemplate : 通過java5簡化後的jdbc連接配接
orm.hibernate.hibernatetemplate : hibernate 2.x的session
orm.hibernate3.hibernatetemplate : hibernate 3.x的session
orm.ibatis.sqlmapclienttemplate : ibatis sqlmap用戶端
orm.jpa.jpatemplate : java持久化api的實體管理器
配置資料源
1、使用jndi資料源
使用<jee:jndi-lookup>元素裝配到spring中。
<!-- 其中jndi-name屬性用于指定jddi中資源的名稱。如果隻設定了jndi-name屬性,那麼就會根據指定的名稱
查找資料源。但是,如果應用程式運作在java應用程式伺服器中,則需要将resource-ref屬性設定為true,
這樣給定的jndi-name将會自動添加java:comp/env/字首
-->
<jee:jndi-lookup jndi-name="/jdbc/spitterds" id="datasource" resource-ref="true" />
2、使用資料源連接配接池
<bean id="datasource" class="org.apache.commons.dbcp.basicdatasource">
<property name="driverclassname" value="org.hsqldb.jdbcdriver" />
<property name="url" value="jdbc:hsqldb:hsql://localhost/spitter.spitter" />
<property name="username" value="sa" />
<property name="password" value="" />
<property name="initialsize" value="5" />
<property name="maxactive" value="10" />
</bean>
前4個屬性是配置basicdatasource所必需的。下面列出了另外的最有用的一些屬性。
initialsize:池啟動時建立的連接配接數量。
maxactive:同一時間可從池中配置設定的最多連接配接數。如果設定為0,表示無限制。
maxidle:池裡不會被釋放的最多空閑連接配接數。如果設定為0,則表示無限制。
maxopenpreparedstatements:在同一時間能夠從語句池中配置設定的預處理語句的最大數量。如果設定為0,表示無限制。
maxwait:在抛出異常之前,池等待連接配接回收的最大時間(當沒有可用連接配接時)。如果設定為-1,表示無限等待。
minevictableidletimemillis:連接配接在池中保持空閑而不回收的最大時間。
minidle:在不建立新連接配接的情況下,池中保持空閑的最小連接配接數。
poolpreparedstatements:是否對預處理語句進行池處理(布爾值)
3、基于jdbc驅動的資料源
在spring中,通過jdbc驅動定義資料源是最簡單的配置方式。spring提供了兩種資料源對象。
drivermanagerdatasource:在每個連接配接請求時都會傳回一個建立的連接配接。與dbcp的basicdatasource不同,由drivermanagerdatasource提供的連接配接兵沒有進行池化管理。
singleconnectiondatasource:在每個連接配接請求時都會傳回同一個連接配接。盡管singleconnectiondatasource不是嚴格意義上的連接配接池資料源,但是你可以将其視為隻有一個連接配接的池。
以上兩個資料源的配置與basicdatasource的配置類似:
<bean id="datasource" class="org.springframework.jdbc.datasource.drivermanagerdatasource">
唯一的差別在于drivermanagerdatasource和singleconnectiondatasource都沒有提供連接配接池功能,是以沒有可配置的池相關的屬性。
singleconnectiondatasource有且隻有一個資料庫連接配接,是以不适用于多線程的應用程式。盡管drivermanagerdatasource支援多線程,但是在每次請求連接配接時都會建立新連接配接,這是以性能為代價的。鑒于以上的這些限制,我強烈建議應該使用資料源連接配接池。
使用jdbc模版
spring為jdbc架構承擔了資源管理和異常處理的工作,進而簡化了jdbc代碼,讓我們隻需編寫從資料庫讀寫資料的必須代碼。
spring将資料通路的樣闆式代碼提取到模版類中。spring為jdbc提供了3個模版類供使用。
1、jdbctemplate:最基本的spring的jdbc模版,這個模版支援最簡答你的jdbc資料庫通路功能以及簡單的索引參數。
2、namedparameterjdbctemplate:使用該模版類執行查詢時,可以将查詢值以命名參數的形式綁定到sql中,而不是使用簡單的索引參數。
3、simplejdbctemplate:該模版類利用java5的一些特性,例如自動裝箱、泛型以及可變參數清單來簡化jdbc模版的使用。
使用simplejdbctemplate通路資料
<bean id="jdbctemplate" class="org.springframework.jdbc.core.simple.simplejdbctemplate">
<constructor-arg ref="datasource"></constructor-arg>
現在,可以将jdbctemplate裝配到dao中兵使用了simplejdbctemplate:
public class jdbcspitterdao implements spitterdao {
private simplejdbctemplate jdbctemplate ;
public void setjdbctemplate(simplejdbctemplate jdbctemplate) {
this.jdbctemplate = jdbctemplate ;
}
}
你還需要裝配jdbcspitterdao的jdbctemplate屬性,如下:
<bean id="spitterdao" class="com.habuma.spitter.persistence.simplejdbctemplatespitterdao">
<property name="jdbctemplate" ref="jdbctemplate" />
下面是基于simplejdbctemplate的addspitter()方法。
public void addspitter(spitter spitter) {
jdbctemplate.update(sql_insert_spitter,
spitter.getusername(),
spitter.getpassword(),
spitter.getfullname(),
spitter.getemail(),
spitter.isupdatebyemail()) ;
spitter.setid(queryforidentity()) ;
使用jdbctemplate也簡化了資料讀取操作。下面是個新方法,在這個方法中使用simplejdbctemplate回調将結果集映射成域對象。
public spitter getspitterbyid(long id) {
return jdbctemplate.queryforobject(sql_select_spitter_by_id,
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.setfullname(rs.getstring(4)) ;
return spitter ;
return null;
}
}, id) ;
在getspitterbyid()方法中使用了simplejdbctemplate的queryforobject()方法來從資料庫查詢spitter。queryforobject()方法有3個參數:
1、string,包含了要從資料庫中查找資料的sql。
2、parameterizedrowmapper對象,用來從resultset中提取值并建構域對象
3、可變參數清單,列出了要綁定到查詢上的索引參數值。
使用命名參數
在上面的addspitter()方法使用了索引參數。這意味着我們需要留意查詢中參數的順序,而在将值傳遞給update()方法的時候要保持正确的順序。如果在修改sql時更改了參數的順序,那麼我們還需要修改參數值的順序。
除了這種方法之外,我們還可以使用命名參數。命名參數可以賦予sql中的每個參數一個明确的名字,在綁定值到查詢語句的時候就通過該名字來引用參數。例如:
private static final string sql_insert_spitter =
"insert into spitter(username, password, fullname) " +
"values (:username, :password, :fullname)" ;
使用命名參數查詢,綁定值的順序就不重要了,我們可以按照名字來綁定值。如果查詢語句發生了變化導緻參數的順序域之前不一緻,我們不需要修改綁定的代碼。
map<string, object> params = new hashmap<string, object>() ;
params.put("username", spitter.getusername()) ;
params.put("password", spitter.getpassword()) ;
params.put("fullname", spitter.getfullname()) ;
jdbctemplate.update(sql_insert_spitter, params) ;
使用spring 的jdbc dao支援類
對于應用程式中的每一個jdbc dao類,我們都需要添加一個simplejdbctemplate屬性以及對應的setter方法,并確定将simplejdbctemplate bean裝配到每個dao的simplejdbctemplate屬性中。如果應用程式中隻有一個dao,這并不算什麼問題,但是如果有多個dao的話,這就會産生大量的重複工作。
一種可行的解決方案就是為所有的dao建立一個通用的父類,在其中會有simplejdbctemplate屬性。然後讓所有的dao類繼承這個類兵使用父類的simplejdbctemplate進行通路。
spring提供了内置的基類。(jdbcdaosupport、simplejdbcdaosupport和namedparameterjdbcdaosupported)
下面是例子:
public class jdbcspitterdao extends simplejdbcdaosupport implements spitterdao{
...
simplejdbcdaosupport通過getsimplejdbctemplate()能夠便捷地通路simplejdbctemplate。例如,addspitter()方法可以這樣改寫:
getsimplejdbctemplate().update(sql_insert_spitter,
在spring中繼承hibernate
在使用過jdbc後,我們還需要一些複雜的特性:
1、延遲加載:我們可以隻抓去需要的資料。
2、預先抓取:這與延遲加載是相對的,借助于預先抓去,我們可以使用一個查詢擷取完整的關聯對象。如果需要purchaseorder及其關聯的lineitem對象,預先抓去的功能可以在一個操作中将它們全部從資料庫中提取出來,這節省了多次查詢的成本。
3、級聯:有時,更改資料庫中的表會同時修改其他表。
spring對多個持久化架構都提供了支援,包括hibernate、ibatis、java資料對象(java data objects,jdo)以及java持久化api。
與spring對jdbc的支援那樣,spring對orm架構的支援提供了與這些架構的內建點以及一些附加的服務,如下所示:
1、spring聲明式事務的內建支援
2、透明的異常處理
3、線程安全的、輕量級的模版類
4、dao支援類
5、資源管理
聲明hibernate的session工廠
使用hibernate的主要接口是org.hibernate.session。session接口提供了基本的資料通路功能,如儲存、更新、删除以及從資料庫加載對象的功能。通過hibernate的session接口,應用程式的dao能夠滿足所有的持久化需求。
擷取hibernate session對象的标準方式是借助于hibernate的sessionfactory接口的實作類。除了一些其他的任務,sessionfactory主要負責hibernate session的打開、關閉以及管理。
1、xml配置
<bean id="sessionfactory" class="org.springframework.orm.hibernate3.localsessionfactorybean">
<property name="datasource" ref="datasource" />
<property name="mappingresources">
<list>
<value>spitter.hbm.xml</value>
</list>
</property>
<property name="hibernateproperties">
<props>
<prop key="dialect">org.hibernate.dialect.hsqldialect</prop>
</props>
2、注解配置
<bean id="sessionfactory" class="org.springframework.orm.hibernate3.annotation.annotationsessionfactorybean">
<property name="packagestoscan" value="com.habuma.spitter.domain" />
就像在localsessionfactorybean中那樣,datasource和hibernateproperties屬性聲明了從哪裡擷取資料庫連接配接以及要使用哪一種資料庫。
這裡不再列出hibernate配置檔案,而是使用packagestoscan屬性告訴spring掃描一個或多個包以查找域類,這些類通過注解方式表明要使用hibernate進行持久化。使用jpa的@entity或@mappedsuperclass注解以及hibernate的@entity注解進行标注的類都會包含在内。
如果願意,我們還可以通過使用annotatedclassed屬性來将應用程式中所有的持久化類以全限定名的方式明确列出:
<property name="annotatedclassed">
<value>com.habuma.spitter.domain.spitter</value>
<value>com.habuma.spitter.domain.spittle</value>
annotatedclassed屬性對于準确指定少量的域類是不錯的選擇。如果你有很多的域類且不想将其全部列出。或者你想自由地添加或溢出域類而不想修改spring配置的話,則使用packagestoscan屬性更合适。
建構不依賴于spring的hibernate代碼
import org.hibernate.session;
import org.hibernate.sessionfactory;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.repository;
@repository
public class hibernatespitterdao implements spitterdao{
private sessionfactory sessionfactory ;
@autowired
public hibernatespitterdao(sessionfactory sessionfactory) {
this.sessionfactory = sessionfactory ;
private session currentsession() {
return sessionfactory.getcurrentsession() ;
public void addspitter(spitter spitter) {
currentsession().save(spitter) ;
public spitter getspitterbyid(long id) {
return (spitter)currentsession().get(spitter.class, id) ;
public void savespitter(spitter spitter) {
currentsession().update(spitter);
我們在類上使用了@repository注解,這會為我們做兩件事情。首先,@repository是spring的另一種構造型注解,它能夠像其他注解一樣被spring的<context:component-scan>所掃描到。這樣就不必明确聲明hibernatespitterdao bean了,隻需在<context:component-scan>配置即可。
除了幫助簡化xml配置以外,@repository還有另外一個用處。讓我們回想一下模版類,它有一項任務就是捕獲平台相關的異常,然後以spring的非檢查型異常形式重新抛出。如果我們使用hibernate上下文session而不是hibernate模版,那麼異常轉換會怎麼處理呢?
為了給不使用模版的hibernate dao添加異常轉換功能,我們隻需在spring應用上下文中添加一個persistenceexceptiontranslationpostprocessorbean:
<bean class="org.springframework.dao.annotation.persistenceexceptiontranslationpostprocessor" />
persistenceexceptiontranslationpostprocessor是一個bean的後置處理程式,它會在所有擁有@repository注解的類上添加一個通知器(advisor),這樣就會捕獲任何平台相關的異常并以spring的非檢查型資料通路異常的形式重新抛出。
spring與java持久化api
配置實體管理器工廠
簡單來說,基于jpa的應用程式使用entitymanagerfactory的實作類來擷取entitymanager執行個體。jpa定義了兩種類型的實體管理器:
1、應用程式管理類型:當應用程式向實體管理器工廠直接請求實體管理器時,工廠會建立一個實體管理器。在這種模式下,程式要負責打開或關閉實體管理器并在事務中對其進行控制。這種方式的實體管理器适合于不運作在java ee容器中的獨立應用程式。
2、容器管理類型:實體管理器由java ee建立和管理。應用程式根本不與實體管理器工廠打交道。相反,實體管理器直接通過注入或jndi來擷取。容器負責配置實體管理器工廠。這種類型的實體管理器最适合用于java ee容器,在這種情況下會希望在persistence.xml指定的jpa配置之外保持一些自己對jpa的控制。
它們兩個的差別在于entitymanager的建立和管理方式。應用程式管理類型的entitymanager是由entitymanagerfactory建立的。而後者是通過persistenceprovider的createentitymanagerfactory()方法得到的。與此相對,容器管理類型的entitymanagerfactory是通過persistenceprovider的createcontainerentitymanagerfactory()方法獲得的。
在容器管理的場景下,spring會擔當容器的角色。
這兩種實體管理器工廠分别由對應的spring工廠bean建立的:
1、localentitymanagerfactorybean生成應用程式管理類型的entitymanagerfactory。
2、localcontainerentitymanagerfactorybean生成容器管理類型的entitymanagerfactory。
使用應用程式管理類型的jpa
對于應用程式管理類型的實體管理器工廠來說,它絕大部配置設定置檔案來源于一個名叫persistence.xml的配置檔案。這個檔案必須位于類路徑下的meta-inf目錄下。
persistence.xml的作用在于定義一個或多個持久化單元。持久化單元是同一個資料源下的一個或多個持久化類。簡單來講,persistence.xml列出了一個或多個的持久化類以及一些其他的配置,如資料源和基于xml的配置檔案。以下是一個典型的persistence.xml檔案,它用于spitter應用程式。
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="1.0">
<persistence-unit name="spitterpu">
<class>com.habuma.spitter.domain.spitter</class>
<class>com.habuma.spitter.domain.spittle</class>
<properties>
<property name="toplink.jdbc.driver" value="org.hsqldb.jdbcdriver" />
<property name="toplink.jdbc.url" value="jdbc:hsqldb:hsql://localhost/spitter/spitter" />
<property name="toplink.jdbc.user" value="sa" />
<property name="toplink.jdbc.password" value="" />
</properties>
</persistence-unit>
</persistence>
因為在persistence.xml檔案中包含了大量的配置資訊,是以在spring中需要配置的就很少了。可以通過以下的<bean>元素在spring中聲明localentitymanagerfactorybean:
<bean id="emf" class="org.springframework.orm.jpa.localentityfactorybean">
<property name="persistenceunitname" value="spitterpu" />
賦給persistenceunitname屬性的值就是persistence.xml中持久化單元的名稱。
使用容器管理類型的jpa
容器管理的jpa采取了一種不同的方式。當在容器中運作時,可以使用容器提供的資訊來生成entitymanagerfactory。
你可以将資料源資訊配置在spring應用上下文中,而不是在persistence.xml中了。例如,下面的<bean>聲明展現了在spring中如何使用localcontainerentitymanagerfactorybean來配置容器管理類型的jpa。
<bean id="emf" class="org.springframework.orm.jpa.localcontainerentitymanagerfactorybean">
<property name="jpavendoradapter" ref="jpavendoradapter" />
jpavendoradapter屬性用于指明所使用的是哪一個廠商的jpa實作。spring提供了多個jpa廠商擴充卡:
1、eclipselinkjpavendoradapter
2、hibernatejpavendoradapter
3、openjpavendoradapter
4、toplinkjpavendoradapter