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

           

          如上圖所示,服務(wù)對(duì)象不再和特定的數(shù)據(jù)訪問(wèn)實(shí)現(xiàn)邦定在一起,使得他們易于測(cè)試。此外,我們是通過(guò)與持久化技術(shù)無(wú)關(guān)的方式來(lái)訪問(wèn)數(shù)據(jù)訪問(wèn)層的,DAO接口不需要暴露他采用什么技術(shù)去訪問(wèn)數(shù)據(jù)。Spring幫你把數(shù)據(jù)訪問(wèn)層從應(yīng)用的其他部分隔離開來(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ù)訪問(wèn)層拋出時(shí),并不需要你的代碼去處理這類異常。這遵循了Spring的一般理念:異常檢查會(huì)使你的代碼到處是不相關(guān)的catchthrows語(yǔ)句,使代碼雜亂無(wú)章。對(duì)數(shù)據(jù)訪問(wèn)異常來(lái)說(shuō),這點(diǎn)尤為正確,因?yàn)槲覀冊(cè)趯戇B接數(shù)據(jù)庫(kù)和操作數(shù)據(jù)庫(kù)表的代碼中,總是充斥著大量的異常。

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

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

          2.一致的DAO支持

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

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

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

          這些類是:

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

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

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

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

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

           

          二、在Spring中使用JDBC

          起初我們?cè)谑褂?/span>Java連接數(shù)據(jù)庫(kù)時(shí)候都使用的是JDBC,剛開始可能是對(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è)類中(http://blog.csdn.net/qutr/archive/2006/09/25/1274826.aspx),還有人設(shè)計(jì)出了數(shù)據(jù)庫(kù)連接池,使得數(shù)據(jù)庫(kù)連接所占有的資源更少,效率更高。這樣的做法,缺點(diǎn)是當(dāng)我們只需要一種操作(插入、刪除、更新、選擇)時(shí)我們要額外的多寫出20—30行代碼。優(yōu)點(diǎn)是,有利于我們對(duì)資源的控制。那么在Spring中我們有了更多更好的選擇。

          1Spring2.0中的包

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

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

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

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

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

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

          2.使用JdbcTemplate

          當(dāng)我們寫JDBC的相關(guān)代碼時(shí)候,總是寫很多的代碼來(lái)維護(hù)資源的連接和釋放,還要處理各種異常情況。寫出的try-catch語(yǔ)句通常會(huì)打亂程序結(jié)構(gòu)。使用Spring JDBC會(huì)使我們的JDBC代碼非常干凈,我們不必再去寫繁瑣的statement和各種查詢語(yǔ)句。提供這一功能的就是JdbcTemplate類,它存在于org.springframework.jdbc.core包下,創(chuàng)建一個(gè)JdbcTemplate類的實(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ù)連接工廠。通過(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ì)象后就可以開始對(duì)數(shù)據(jù)庫(kù)進(jìn)行各種查詢和更新了。

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

          在《Spring In Action》一書中作者介紹了好幾種方法,這里只展示一種。首先,設(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類中,在適當(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》一書中都講解的比較詳細(xì)這里就不再說(shuō)了。

          值得注意的是在Spring2.0中為我們提供了一個(gè)NamedParameterJdbcTemplate類,該類為JDBC查詢提供了帶命名參數(shù)的占位符,而不止是JDBC自己的,這樣使用JDBC的時(shí)候,也可以很容易的構(gòu)造出來(lái)帶占位符的動(dòng)態(tài)條件查詢,而不是參數(shù)值帶入方式的拼接SQL字符串了。(引自:Spring2.0的新特性點(diǎn)評(píng))確實(shí)也是這樣,而且在Spring2.0文檔中,作者用了較大篇幅介紹了該類。察看Spring2.0的原代碼(該類在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類。關(guān)于Spring JDBC的更多更詳細(xì)的內(nèi)容請(qǐng)參看Spring2.0官方文檔,上面講解的非常透徹,代碼實(shí)例也非常明了。

          還有一個(gè)類值得我們關(guān)注:SimpleJdbcTemplate類,該類是JdbcTemplate類的一個(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)悍。

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

           

          四、小結(jié)

          Spring的持久化技術(shù)是多種多樣的,但是不管用什么樣的持久化技術(shù),Spring的目標(biāo)就是讓它對(duì)應(yīng)用系統(tǒng)的其他部分來(lái)說(shuō)是透明的。這樣我們?cè)诰帉懗志脤拥臅r(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)  編輯  收藏 所屬分類: Spring
          主站蜘蛛池模板: 大连市| 略阳县| 女性| 黄陵县| 丰镇市| 沙雅县| 渝中区| 东丰县| 巢湖市| 托克逊县| 陇川县| 巨野县| 孝义市| 汉沽区| 汉寿县| 高碑店市| 临高县| 疏勒县| 济源市| 安国市| 淮阳县| 镇巴县| 东台市| 洪江市| 禄丰县| 巴楚县| 娱乐| 宜兰市| 文安县| 内丘县| 镇沅| 古交市| 新平| 子洲县| 博客| 色达县| 永安市| 峨山| 金川县| 奉化市| 台州市|