隨筆-28  評(píng)論-15  文章-81  trackbacks-0

          先是一個(gè)好消息,在2006年國(guó)慶期間,Spring2.0正式版(http://www.springframework.org/download)和Spring2.0中文文檔(http://spring.jactiongroup.net/viewtopic.php?t=2279&sid=0906ae6a913537b249e501db5b54c181)終于發(fā)布了。

          在前面的學(xué)習(xí)之后,我們已經(jīng)了解了Spring框架的核心,在本章中作者向我們講述了Spring的持久化層,向我們介紹了Srping對(duì)JDBC的支持。還向讀者介紹了Spring如何與當(dāng)前流行的一些ORM框架,如HibernateJDOOJB、和iBATIS等集成。

           

          一、學(xué)習(xí)SpringDAO理念

          DAO代表數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象(Data Access Object)。它完美地描述了在一個(gè)應(yīng)用中DAO的角色。DAO的存在提供了讀寫(xiě)數(shù)據(jù)庫(kù)中數(shù)據(jù)的一種方法。只要把這個(gè)功能通過(guò)接口暴露,應(yīng)用的其他部分就可以通過(guò)這個(gè)接口來(lái)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)了。下圖展示了設(shè)計(jì)數(shù)據(jù)訪(fǎng)問(wèn)層的合適途徑:

           

          如上圖所示,服務(wù)對(duì)象不再和特定的數(shù)據(jù)訪(fǎng)問(wèn)實(shí)現(xiàn)邦定在一起,使得他們易于測(cè)試。此外,我們是通過(guò)與持久化技術(shù)無(wú)關(guān)的方式來(lái)訪(fǎng)問(wèn)數(shù)據(jù)訪(fǎng)問(wèn)層的,DAO接口不需要暴露他采用什么技術(shù)去訪(fǎng)問(wèn)數(shù)據(jù)。Spring幫你把數(shù)據(jù)訪(fǎng)問(wèn)層從應(yīng)用的其他部分隔離開(kāi)來(lái)的一種方法是:提供一套貫穿整個(gè)DAO框架的一致的分級(jí)異常體系。

          1.理解SpringDataAccessException

          SpringDAO框架沒(méi)有拋出與特定技術(shù)相關(guān)的異常,Spring提供了一種方便的方法,把特定于某種技術(shù)的異常,如SQLException,轉(zhuǎn)化為自己的異常,這種異常屬于以DataAccessException為異常層次。

          DataAccessExceptionRuntimeException,所以它是一個(gè)無(wú)需檢測(cè)的異常。意思是說(shuō),當(dāng)他們被數(shù)據(jù)訪(fǎng)問(wèn)層拋出時(shí),并不需要你的代碼去處理這類(lèi)異常。這遵循了Spring的一般理念:異常檢查會(huì)使你的代碼到處是不相關(guān)的catchthrows語(yǔ)句,使代碼雜亂無(wú)章。對(duì)數(shù)據(jù)訪(fǎng)問(wèn)異常來(lái)說(shuō),這點(diǎn)尤為正確,因?yàn)槲覀冊(cè)趯?xiě)連接數(shù)據(jù)庫(kù)和操作數(shù)據(jù)庫(kù)表的代碼中,總是充斥著大量的異常。

              一般情況下,我們的數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)API總是要拋出一個(gè)有意義的異常。JDO有它自己的異常層次。下圖是Spring2.0中定義的異常層次。

          上圖所包括的異常類(lèi)只是整個(gè)龐大的DataAccessException異常層次中的一部分。

          2.一致的DAO支持

          不管我們采用什么技術(shù),某些數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)的步驟是必須的。如數(shù)據(jù)庫(kù)連接,操作,操作完后釋放連接。這些都是固定步驟。Spring把數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)流程中的固定部分和可變部分分開(kāi),分別映射成兩個(gè)截然不同的類(lèi):模板(Template)和回調(diào)(Callback)。模板管理流程的固定部分,而在回調(diào)處填寫(xiě)實(shí)現(xiàn)細(xì)節(jié)。如下圖所示:

          在上圖中Spring的模板類(lèi)處理數(shù)據(jù)訪(fǎng)問(wèn)的不變部分事務(wù)控制、資源管理以及異常處理。回調(diào)接口的實(shí)現(xiàn)定義了特定于應(yīng)用的部分創(chuàng)建statement,邦定參數(shù)以及整理結(jié)果集(ResultSet)。這樣我們只需要關(guān)心數(shù)據(jù)訪(fǎng)問(wèn)的邏輯就可以了。

          為了便于以一種一致的方式使用各種數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)技術(shù),如JDBCJDOHibernateSpring提供了一套抽象DAO類(lèi)供我們進(jìn)行擴(kuò)展。這些抽象類(lèi)提供了一些方法,通過(guò)它們我們可以獲得與當(dāng)前數(shù)據(jù)訪(fǎng)問(wèn)技術(shù)相關(guān)的數(shù)據(jù)源和其他配制信息。

          這些類(lèi)是:

          l         JdbcDaoSupport – JDBC數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象的基類(lèi)。

          l         HibernateDaoSupport – Hibernate數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象的基類(lèi)。

          l         JdoDaoSupport – JDO數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象的基類(lèi)。

          l         JpaDaoSupport – JPA數(shù)據(jù)訪(fǎng)問(wèn)的基類(lèi)。

          對(duì)于以上四個(gè)類(lèi),更詳細(xì)的內(nèi)容請(qǐng)參看Spring2.0第十章。

           

          二、在Spring中使用JDBC

          起初我們?cè)谑褂?/span>Java連接數(shù)據(jù)庫(kù)時(shí)候都使用的是JDBC,剛開(kāi)始可能是對(duì)數(shù)據(jù)庫(kù)表作一個(gè)操作就會(huì)連接一次數(shù)據(jù)庫(kù),然后再將數(shù)據(jù)庫(kù)連接關(guān)閉。大概是這樣的步驟:連接數(shù)據(jù)庫(kù)-- 對(duì)某表進(jìn)行操作關(guān)閉數(shù)據(jù)庫(kù),后來(lái)一些人對(duì)數(shù)據(jù)庫(kù)的操作進(jìn)行了封裝,將數(shù)據(jù)庫(kù)的連接操作關(guān)閉等動(dòng)作放到一個(gè)類(lèi)中(http://blog.csdn.net/qutr/archive/2006/09/25/1274826.aspx),還有人設(shè)計(jì)出了數(shù)據(jù)庫(kù)連接池,使得數(shù)據(jù)庫(kù)連接所占有的資源更少,效率更高。這樣的做法,缺點(diǎn)是當(dāng)我們只需要一種操作(插入、刪除、更新、選擇)時(shí)我們要額外的多寫(xiě)出20—30行代碼。優(yōu)點(diǎn)是,有利于我們對(duì)資源的控制。那么在Spring中我們有了更多更好的選擇。

          1Spring2.0中的包

          Spring JDBC抽象框架由四個(gè)包構(gòu)成:core dataSourceobject以及support

          org.springframework.jdbc.core包由JdbcTemplate類(lèi)以及相關(guān)的回調(diào)接口(callback interface)和類(lèi)組成。

          org.springframework.jdbc.datasource包由一些用來(lái)簡(jiǎn)化DataSource訪(fǎng)問(wèn)的工具類(lèi),以及各種DataSource接口的簡(jiǎn)單實(shí)現(xiàn)(主要用于單元測(cè)試以及在J2EE容器之外使用JDBC)組成。工具類(lèi)提供了一些靜態(tài)方法,諸如通過(guò)JNDI獲取數(shù)據(jù)連接以及在必要的情況下關(guān)閉這些連接。它支持綁定線(xiàn)程的連接,比如被用于DataSourceTransactionManager的連接。

          org.springframework.jdbc.object包由封裝了查詢(xún)、更新以及存儲(chǔ)過(guò)程的類(lèi)組成,這些類(lèi)的對(duì)象都是線(xiàn)程安全并且可重復(fù)使用的。它們類(lèi)似于JDO,與JDO的不同之處在于查詢(xún)結(jié)果與數(shù)據(jù)庫(kù)是斷開(kāi)連接的。它們是在org.springframework.jdbc.core包的基礎(chǔ)上對(duì)JDBC更高層次的抽象。

          org.springframework.jdbc.support包提供了一些SQLException的轉(zhuǎn)換類(lèi)以及相關(guān)的工具類(lèi)。

          JDBC處理過(guò)程中拋出的異常將被轉(zhuǎn)換成org.springframework.dao包中定義的異常。因此使用Spring JDBC進(jìn)行開(kāi)發(fā)將不需要處理JDBC或者特定的RDBMS才會(huì)拋出的異常。所有的異常都是unchecked exception,這樣我們就可以對(duì)傳遞到調(diào)用者的異常進(jìn)行有選擇的捕獲

          2.使用JdbcTemplate

          當(dāng)我們寫(xiě)JDBC的相關(guān)代碼時(shí)候,總是寫(xiě)很多的代碼來(lái)維護(hù)資源的連接和釋放,還要處理各種異常情況。寫(xiě)出的try-catch語(yǔ)句通常會(huì)打亂程序結(jié)構(gòu)。使用Spring JDBC會(huì)使我們的JDBC代碼非常干凈,我們不必再去寫(xiě)繁瑣的statement和各種查詢(xún)語(yǔ)句。提供這一功能的就是JdbcTemplate類(lèi),它存在于org.springframework.jdbc.core包下,創(chuàng)建一個(gè)JdbcTemplate類(lèi)的實(shí)例相當(dāng)簡(jiǎn)單:

          JdbcTemplate jdbcTem = new JdbcTemplate(dataSource);

          關(guān)于dataSourceDataSource的一個(gè)實(shí)例,DataSourcejavax.sql包下,他是一個(gè)interface所以我們要實(shí)現(xiàn)他,DataSourceJDBC規(guī)范的一部分,它被視為一個(gè)通用的數(shù)據(jù)庫(kù)連接工廠(chǎng)。通過(guò)使用DataSource ContainerFramework可以將連接池以及事務(wù)管理的細(xì)節(jié)從應(yīng)用代碼中分離出來(lái)。在這里我們使用DriverManagerDataSource,不過(guò)DataSource有多種實(shí)現(xiàn),詳細(xì)說(shuō)明可以參考Spring參考手冊(cè)(官方文檔)。看下面的代碼示例:

              DriverManagerDataSource dataSource = new DriverManagerDataSource();

              dataSource.setDriverClassName("com.mysql.jdbc.Driver");

              dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=gb2312&autoReconnect=true");

              dataSource.setUsername("root");

              dataSource.setPassword("");

          當(dāng)我們?cè)O(shè)置好DataSource和得到JdbcTemplate對(duì)象后就可以開(kāi)始對(duì)數(shù)據(jù)庫(kù)進(jìn)行各種查詢(xún)和更新了。

          3.寫(xiě)入數(shù)據(jù)

          在《Spring In Action》一書(shū)中作者介紹了好幾種方法,這里只展示一種。首先,設(shè)置數(shù)據(jù):

              private DataSource dataSource;

              private JdbcTemplate jdbcTemlate = new JdbcTemplate(dataSource);

              private Person person = new Person();

             

              publicvoid setValue(PreparedStatement ps) throws SQLException

              {

                 ps.setInt(0, person.getId());

                 ps.setString(1, person.getFirstName());

                 ps.setString(2, person.getLastName());

              }//end setValue(...)

             

              publicint insertPerson(Person person)

              {

                 String insertSql = "insert into person (id, firstName, lastName) values(?, ?, ?)";

                 Object[] params = new Object[] {person.getId(), person.getFirstName(), person.getLastName()};

                 returnjdbcTemlate.update(insertSql, params);

              }//end insertPerson(...)

          最后一個(gè)insertPerson函數(shù)實(shí)現(xiàn)了插入一條記錄的操作,可以看到非常的簡(jiǎn)單。當(dāng)我們使用了JdbcTemplate后我們不必去擔(dān)心數(shù)據(jù)庫(kù)的關(guān)閉,再JdbcTemplate類(lèi)中,在適當(dāng)?shù)牡胤揭呀?jīng)為我們將數(shù)據(jù)庫(kù)關(guān)閉了。

          使用BatchPreparedStatementSetter這個(gè)接口,一次插入多條記錄。該接口有兩個(gè)方法:

          void setValues(PreparedStatement ps, int i) throws SQLException;

          int getBatchSize();其中getBatchSize()告訴JdbcTemplate到底有多少語(yǔ)句要?jiǎng)?chuàng)建。看下面的代碼:

              publicint[] updatePersons(final List persons)

              {

                 String insertSql = "insert into person (id, firstName, lastName) values(?, ?, ?)";

                 BatchPreparedStatementSetter setter = new BatchPreparedStatementSetter(){

                     publicint getBatchSize(){

                        return persons.size();

                     }

                    

                     publicvoid setValues(PreparedStatement ps, int index)throws SQLException {

                        Person person = (Person)persons.get(index);

                        ps.setInt(0, person.getId());

                        ps.setString(1, person.getFirstName());

                        ps.setString(2, person.getLastName());

                     }

                 };

                

                 returnjdbcTemlate.batchUpdate(insertSql, setter);

              }//end updatePersons(...)

          這樣就實(shí)現(xiàn)了一次插入多個(gè)記錄。

          4.讀數(shù)據(jù)

          JDBC中取得一條記錄的結(jié)果需要通過(guò)ResultSet。在Spring JDBC中已經(jīng)幫我們處理了這些。通過(guò)實(shí)現(xiàn)RowCallbackHandler接口中僅有的一個(gè)方法來(lái)完成。該方法為:

          void processRow(ResultSet rs) throws SQLException;

          下面的代碼展示了如何使用RowCallbackHandler接口來(lái)選擇一條記錄。

              public Person getPerson(final Integer id)

              {

                 String sql = "select id, first_name, last_name from person where id = ?";

                 final Person person = new Person();

                 final Object[] params = new Object[] {id};

                 jdbcTemplate.query(sql, params, new RowCallbackHandler(){

                     publicvoid processRow(ResultSet rs) throws SQLException{

                        person.setId(rs.getInt("id"));

                        person.setFirstName(rs.getString("first_name"));

                        person.setLastName(rs.getString("last_name"));

                     }

                 });

                 return person;

              }//end getPerson(...)

          利用Spring JDBC提供的RowMapper接口我們可以把ResultSet中的一條記錄映射成一個(gè)對(duì)象。這個(gè)在《Spring In Action》一書(shū)中都講解的比較詳細(xì)這里就不再說(shuō)了。

          值得注意的是在Spring2.0中為我們提供了一個(gè)NamedParameterJdbcTemplate類(lèi),該類(lèi)為JDBC查詢(xún)提供了帶命名參數(shù)的占位符,而不止是JDBC自己的,這樣使用JDBC的時(shí)候,也可以很容易的構(gòu)造出來(lái)帶占位符的動(dòng)態(tài)條件查詢(xún),而不是參數(shù)值帶入方式的拼接SQL字符串了。(引自:Spring2.0的新特性點(diǎn)評(píng))確實(shí)也是這樣,而且在Spring2.0文檔中,作者用了較大篇幅介紹了該類(lèi)。察看Spring2.0的原代碼(該類(lèi)在org.springframework.jdbc.core.namedparam包下)可以看到:

              public NamedParameterJdbcTemplate(DataSource dataSource) {

                 Assert.notNull(dataSource, "The [dataSource] argument cannot be null.");

                 this.classicJdbcTemplate = new JdbcTemplate(dataSource);

              }

          他事實(shí)上調(diào)用了JdbcTemplate類(lèi)。關(guān)于Spring JDBC的更多更詳細(xì)的內(nèi)容請(qǐng)參看Spring2.0官方文檔,上面講解的非常透徹,代碼實(shí)例也非常明了。

          還有一個(gè)類(lèi)值得我們關(guān)注:SimpleJdbcTemplate類(lèi),該類(lèi)是JdbcTemplate類(lèi)的一個(gè)包裝器(wrapper),它利用了Java 5的一些語(yǔ)言特性,特別適合在JDK5下編程的人。下面引用一個(gè)例子:

          public Actor findActor(long id)

          {

              String sql = "select id, first_name, last_name from T_ACTOR where id = ?";

              ParameterizedRowMapper<Actor> mapper = new ParameterizedRowMapper()<Actor> {

                  public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {

                      Actor actor = new Actor();

                      actor.setId(rs.getLong("id"));

                      actor.setFirstName(rs.getString("first_name"));

                      actor.setLastName(rs.getString("last_name"));

                      return actor;

                  }

              };

              SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(this.getDataSource());

              return simpleJdbcTemplate.queryForObject(sql, mapper, id);

          }

           

          三、SpringORM框架支持

          Spring除了很好的封裝了JDBC外還支持以下持久層框架:

          l         Hibernatehttp://www.hibernate.org/

          l         JDOhttp://java.sun.com/products/jdo/

          l         iBATIShttp://ibatis.apache.org/

          l         OJBhttp://db.apache.org/ojb/

          在我目前手頭上的工作來(lái)看,有了JDBC已經(jīng)足夠我用了。但是學(xué)習(xí)是無(wú)止境的,看網(wǎng)上那幫人整天這個(gè)框架那個(gè)框架的大談特談,學(xué)到這里的我肯定不會(huì)停止不前的。我的決定是學(xué)習(xí)Hibernate,當(dāng)然JDO是標(biāo)準(zhǔn),iBATISOJB也是非常的強(qiáng)悍。

          寫(xiě)到這里下面應(yīng)該接著寫(xiě)以上框架的一種或幾種了,那么按照我的計(jì)劃應(yīng)該最少寫(xiě)點(diǎn)Hibernate的內(nèi)容,但是很不幸的是由于太忙Hibernate學(xué)的還非常淺薄有限。思前想后,我應(yīng)該停下來(lái)了。因?yàn)樽源颉?/span>Spring In Action》學(xué)習(xí)筆記開(kāi)寫(xiě)以來(lái),文章一篇比一篇質(zhì)量差,貼到Blog上的東西雖然是督促自己學(xué)習(xí)的,但是肯定是有人看的。為了對(duì)得起自己,對(duì)得起看文章的人,我覺(jué)定還是把質(zhì)量提高比較重要。所以到這里我要打住了。等在實(shí)踐中有了經(jīng)驗(yàn)和領(lǐng)悟,我會(huì)寫(xiě)一個(gè)關(guān)于Hibernate的專(zhuān)題。希望這一天早日到來(lái)。

           

          四、小結(jié)

          Spring的持久化技術(shù)是多種多樣的,但是不管用什么樣的持久化技術(shù),Spring的目標(biāo)就是讓它對(duì)應(yīng)用系統(tǒng)的其他部分來(lái)說(shuō)是透明的。這樣我們?cè)诰帉?xiě)持久層的時(shí)候可以減輕工作量,減少出錯(cuò)的幾率。使我們對(duì)于數(shù)據(jù)庫(kù)的操作更為方便和簡(jiǎn)單。

          最后推薦兩片文章:

          n         Spring2.0的新特性點(diǎn)評(píng)( http://www.javaeye.com/topic/25530

          n         無(wú)責(zé)任評(píng)論SpringJava企業(yè)應(yīng)用(http://www.javaeye.com/topic/25556

          posted on 2007-10-23 21:47 譚明 閱讀(752) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Spring
          主站蜘蛛池模板: 河池市| 区。| 平陆县| 吉首市| 阳山县| 重庆市| 个旧市| 阳西县| 宿迁市| 通州市| 宣武区| 南部县| 沾化县| 阜南县| 宁夏| 永济市| 永宁县| 张家港市| 芒康县| 衡阳市| 余江县| 仁寿县| 河北省| 利辛县| 都匀市| 莎车县| 乌海市| 赤壁市| 阳新县| 石泉县| 安塞县| 湘西| 安康市| 拉萨市| 莫力| 册亨县| 泸溪县| 宁陵县| 闽侯县| 呼伦贝尔市| 浦北县|