天天看點

Spring征服資料庫

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