Dict.CN 在線詞典, 英語學習, 在線翻譯

          都市淘沙者

          荔枝FM Everyone can be host

          統計

          留言簿(23)

          積分與排名

          優秀學習網站

          友情連接

          閱讀排行榜

          評論排行榜

          使用Spring的JdbcTemplate實現DAO(zhuan)

          直接使用JDBC操作數據庫來實現DAO,相對于使用持久層框架(Hibernate等) ,可以減少映射等帶來的性能損失。
          一般而言,使用JDBC來操作數據庫,無非是以下幾個步驟:
              獲取數據源
              取得數據庫連接
              執行SQL語句
              處理執行結果和異常
              釋放數據庫連接
          這些步驟是每次進行數據庫操作都必須進行的。很顯然,可以使用模板模式來簡化設計,將執行SQL語句的部分抽象出來,其它的步驟由模板自動完成。
          雖然設計這樣的模板并不算復雜,但是現在我們完全無需自己來實現,應為Spring已經為我們提供了一個非常實用的模板--JdbcTemplate。它位于Spring包org.springframework.jdbc.core下,是Spring的jdbc工具包的核心類之一。使用JdbcTemplate的前提是必須為其提供數據源(DataSource),并通過實用類DataSourceUtils來安全地獲取和釋放數據庫連接(Connection對象)。可以說已經是一個完美的實現,因此我們只要放心地使用就可以了。

          總體思路
              (1)數據源通過Spring的容器來提供;
              (2)DAO通過靜態方式從Spring容器中獲取;
              (3)針對接口編程;
              (4)提供數據操作父類,簡化具體DAO實現。

          一、DAO地實現
              遵循Spring的良好編程風格,針對接口編程。同時,為了簡化對接口的實現,還提供了一個所有實現類的父類,提供了具體的數據庫操作方法,這些方法當然就是使用JdbcTemplate來實現的了。

          1、Dao父類
              主要目的是獲取數據源,為子類提供具體的數據庫操作方法:

          public class Dao {
              
              
          /**
               * 日志
               
          */

              
          protected Log log = LogFactory.getLog(this.getClass().getName());
              
              
          /**
               * 執行查詢SQL文
               *  
               * 
          @param strSql
               
          */

              
          protected void doQuery(String strSql) {

                  JdbcTemplate jt 
          new JdbcTemplate(getDataSource());    //使用
          JdbcTemplate
                  
                  
          try {
                      List result 
          = jt.queryForList(strSql);
                      
          this.resultList = result;
                      
                      
          this.resultCount = result.size();
                      
                      log.debug(logHeader 
          + strSql);
                      
                  }
           catch (DataAccessException de) {
                      
                      log.error(logHeader 
          + de);
                  }

                  
              }
              
              
              
          /**
               * 執行更新SQL文
               * 
               * 
          @param strSql
               
          */

              
          protected boolean doUpdate(String strSql) {
                  
                  JdbcTemplate jt 
          = new JdbcTemplate(getDataSource());
                  
                  
          try {
                      
          this.affectedRows = jt.update(strSql);
                      
                      log.debug(logHeader 
          + strSql);
                      
                      
          return true;
                      
                  }
           catch (DataAccessException de) {
                      
                      log.error(logHeader 
          + de);
                      
                      
          return false;
                  }

              }


              
          /**
               * 獲取執行結果的一行
               * 
               * 
          @return 一行結果用Map保存
               
          */

              
          protected Object getResult() {
                  
          if (this.resultList == null || this.resultList.size() == 0{
                      
          return null;
                  }
           else {
                      
          return this.resultList.get(0);
                  }

              }

              
              
          /**
               * 獲取查詢結果
               * 
               * 
          @return 查詢結果以List保存
               
          */

              
          protected List getResultList() {
                  
          return this.resultList;
              }

              
              
          /**
               * 獲取更新所影響的行記錄數
               * 
               * 
          @return 整數
               
          */

              
          protected int getAffectedRows() {
                  
          return this.affectedRows;
              }

                  
              
          /**
               * 獲取查詢結果總行數
               * 
               * 
          @return 整數
               
          */

              
          protected long getResultCount() {
                  
          return this.resultCount;
              }
              

              
          /** 日志頭部 */
              
          protected String logHeader = LogUtil.getLogHeader();
              
              
          /**
               * 
          @return dataSource
               
          */

              
          public DataSource getDataSource() {
                  
          return dataSource;
              }


              
          /**
               * 
          @param dataSource 設置 dataSource
               
          */

              
          public void setDataSource(DataSource dataSource) {
                  
          this.dataSource = dataSource;   //獲取數據源
              }
              
              
              
          //-------------------------------------------------------------------------

              
          private List resultList = null;
              
          private long resultCount = 0;
              
          private int affectedRows = 0;    
              
          private DataSource dataSource = null;
          }

          此處為dataSource提供了get/set方法,可以從使用Spring的Ioc中獲益,即可以使用配置的方法為之選擇數據源(本文的實現方式);同時,也可以通過代碼手工提供數據源。

          2、UserDao接口定義:

          public interface UserDao {
              
              
          /**
               * 根據用戶ID,獲取User對象
               * 
               * 
          @param 用戶ID
               * 
          @return User対象
               
          */

              
          public User getUserByLoginId(String loginId);

              
          }

          就一個方法,僅僅是示例而已 ^_^

          3、UserDaoImpl實現:

          public class UserDaoImpl extends Dao implements UserDao {

              
          /*
               * (non-Javadoc)
               * @see my.test.dao.UserDao#getUserByLoginId(java.lang.String)
               
          */

              
          public User getUserByLoginId(String loginId) {
                  
                  User user 
          = null;
                  
                  doQuery(Sql.S_M_MEMBER_01(loginId));
                  
          if (getResultCount() > 0{            
                      user 
          = new User();
                      
                      Map result 
          = (Map) getResult();
                      user.setMemberId(String.valueOf(result.get(
          "MEMBER_ID")));
                     
          //以下省略
                  }

                  
                  
          return user;
              }


          }

          這里只是簡單地實現了平面(二維的數據庫)和立體(對象)的轉換,并沒有涉及到對關系數據庫中的關系的轉化,這些都要靠自己去實現,還是有些不方便。自己實現的麻煩之處就是要手工編寫所有的SQL語句...但一切都在自己的掌控制中,也沒什么不好,呵呵。
          現在看看紅色的部分用起來是不是簡便了很多了。

          羅羅嗦嗦地一堆代碼,還不如看圖來得清爽:


          二、DAO的使用
              既然使用了JdbcTemplate,為什么不再多使用一點Spring的功能呢?比如通過配置為DAO提供數據源等。但問題是,整個應用系統如果沒有使用Spring的容器來管理,如何才能使用Ioc功能呢?還好,可以將Spring的應用上下文(ApplicationContext)單獨地使用。我們可以將DAO作為單獨的bean分別定義,然后從ApplicationContext中取得這些bean的實例。Spring會根據定義為我們自動生成所需的DAO的實例。只要在定義的時候,為DAO提供Datasoure即可實現數據源的自動配置了。
          1、daoBeans.xml

          <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

          <beans>
              
              
          <bean id="dataSource" 
                  class
          ="org.springframework.jdbc.datasource.DriverManagerDataSource"
                  destroy-method
          ="close">
                  
          <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
                  
          <property name="url" value="jdbc:oracle:thin:@192.168.***.***:1521:***"/>
                  
          <property name="username" value="username"/>
                  
          <property name="password" value="password"/>
                  
          <property name="validationQuery" value="SELECT 1 FROM DUAL"/>
              
          </bean>

              
          <bean id="userDao" class="my.test.dao.impl.UserDaoImpl">
                  
          <property name="dataSource">      <--為UserDao設置數據源
                      
          <ref bean="dataSource"/>     
                  
          </property>
              
          </bean>

          </beans>

          使用了Spring的數據源管理功能,通過定義的方式為UserDao提供了數據源(在生成UserDao實例的時候,setDataSource方法就會被調用從而將dataSource載入)。

          2、DAO獲取工具的實現
              編寫了一個工具類,提供根據DAO名(daoBeans.xml中定義的名字)獲取DAO對象的靜態方法。

          public class DaoBeanFactory {
              
              
          private static Log log = LogFactory.getLog(DaoBeanFactory.class.getName());
              
              
          private static final String DAO_BEANS= "/daoBeans.xml";
              
          private static ApplicationContext ctx = null;
              
              
          static {
                  ctx 
          = new ClassPathXmlApplicationContext(DAO_BEANS);
                  log.debug(DAO_BEANS 
          + "被成功載入。");
              }

              
              
          public static Object getDao(String daoName){        
                  
          return ctx.getBean(daoName);
              }

          }

          這里首先通過讀取daoBeans.xml初始化應用上下文(ApplicationContext )。由于是靜態方法,只會在第一次被使用時執行一次;然后就可從上下文中獲取所需要的指名DAO對象了:

          private UserDao userDao = (UserDao) DaoBeanFactory.getDao("userDao");


          到此為止,可以說從實現到使用都已經大功告成了,happy一下。

          三、進一步改進
              并不是說一切都是完美的。比如,第一次執行DaoBeanFactory.getDao方法時,會進行應用上下文的初始化,耗時較多;想改善數據源的管理,比如使用連接池;更進一步,如果大部分的工作都是進行查詢,想提高系統的查詢效率,該如何呢?
              時間不夠,請等待下回分解。


          1、對ApplicationContex初始化的改進
              DAO的獲取工具類DaoBeanFactory首次被使用的時候會花上幾秒鐘執行ApplicationContex的初始化工作。如果用戶在進行登錄的時候,輸入了用戶名和密碼之后要等上幾秒鐘后才被告知可以進入了,這樣的系統給人的第一印象就是--怎么這么慢啊!為了盡量避免這種情況出現,既然是初始化而且還耗時,我們就應該盡量將這種初始化工作放到系統啟動的時候去做。
              實現的方法很多,我就簡單寫了一個初始化Servlet,在Servlet的init中調用一次DaoBeanFactory即可觸發ApplicationContex的初始化:

          public class AppInitServlet extends HttpServlet {

              
          /**
               * 
               
          */

              
          private static final long serialVersionUID = 2987034642754889458L;
              
              
          public void init() {
                  log.info(
          "應用系統啟動開始…");
                  
                  
          if (DaoBeanFactory.getDao("dataSource"!= null{
                      log.info(
          "應用系統啟動正常終了。");
                  
                  }
           else {        
                      log.info(
          "應用系統啟動異常終了。");
                  }

              }

              
              
          private Log log = LogFactory.getLog(AppInitServlet.class.getName());

          }

          然后在web.xml中設置為啟動執行就可以了:

             <servlet>
                  
          <servlet-name>AppInit</servlet-name>
                  
          <servlet-class>my.test.common.AppInitServlet</servlet-class>
                  
          <load-on-startup>1</load-on-startup>
              
          </servlet>

          重新啟動時,就可以看到Log打印出的啟動信息。再次登錄,快多了吧!

          2、更換數據源管理
              dataSource使用的是Spring的DriverManagerDataSource。一般情況下,這已經足夠了。但它沒有提供連接池功能,對系統的整體性能有所影響。于是,更換具有連接池功能的吧。我用的是開源的DBCP連接池,配置也很簡便:

              <bean id="dataSource"
                  class
          ="org.apache.commons.dbcp.BasicDataSource"
                  destroy-method
          ="close">
                  
          <property name="driverClassName">
                      
          <value>oracle.jdbc.driver.OracleDriver</value>
                  
          </property>
                  
          <property name="url">
                      
          <value>jdbc:oracle:thin:@192.168.4.***:1521:***</value>
                  
          </property>
                  
          <property name="username">
                      
          <value>username</value>
                  
          </property>
                  
          <property name="password">
                      
          <value>password</value>
                  
          </property>
                  
          <property name="validationQuery">            
                      
          <value>SELECT 1 FROM DUAL</value>
                  
          </property>                                                       
                  
          <property name="testOnBorrow">               
                      
          <value>true</value>                                   
                  
          </property>                                                       
              
          </bean>

          這里也有要注意的地方。當數據庫服務器重新啟動或者出現故障后,連接池中的連接是要被清空的。但是DBCP似乎沒有去自動檢測連接是否還有效。為了安全起見,最好在使用前測試其有效性。灰色背景部分的配置就是這個目的。

          3、增加查詢緩存
              在使用Hibernate的時候,可以很容易地獲得緩存帶來的系統性能優化。這里,我了達到相似的效果,也增加了緩存。其實Spring已經提供了使用各種緩存的接口,它位于org.springframework.cache.ehcache包下。
              如何實現緩存功能呢?當每個DAO執行查詢方法的時候,就先到緩存中查找歷史緩存數據,存在則返回,不存在才去訪問數據庫。勢必要為每一個這樣的查詢方法都做一個if判斷。若是日后想取消或撤換緩存的時候,這就成了一個艱巨的修改任務。這就和事務管理類似,我可以使用也可以不使用。很自然地,就想到了使用Spring的AOP功能,對每個查詢方法作橫切處理,增加對緩存的判斷。
              很幸運,這個世界上有很多很勤快的人,讓我們可以變得越來越懶。Pieter Coucke已經實現了一個非常實用的AOP Cache。根據他提供的例子,很容易就實現了一個基于AOP的DAO緩存。首先下載Spring AOP Cache的jar文件以及EHCache的jar文件(我使用的是EHCache,當然也可以使用其它的),加入到系統工程中。剩下的任務就是寫配置文件:

              <!-- Cache Configuration -->    
              
          <bean id="cacheInterceptor" class="org.springframework.aop.interceptor.cache.EHCacheInterceptor">
                  
          <property name="refreshPeriods">
                      
          <props>
                          
          <prop key="my.test.dao.UserDao@getUserByLoginId">900</prop>
                      
          </props>
                  
          </property>

                  
          <property name="identifiers">
                      
          <props>
                          
          <prop key="java.lang.String">toString</prop>
                      
          </props>
                  
          </property>
              
          </bean>
              
              
          <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
                  
          <property name="advice">
                      
          <ref bean="cacheInterceptor"/>
                  
          </property>
                  
          <property name="patterns">
                      
          <list>
                          
          <value>.*get.*</value>         <!-- 這里就是匹配查詢方法了 -->
                      
          </list>
                  
          </property>
              
          </bean>
              
          <bean id="cacheProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
                  
          <property name="beanNames">
                      
          <value>*Dao</value>
                  
          </property>
                  
          <property name="interceptorNames">
                      
          <list>
                          
          <value>advisor</value>
                      
          </list>
                  
          </property>
              
          </bean>

          重新啟動后,登錄完成后再登錄一次試試看?

          cache.CacheInterceptor (CacheInterceptor.java:229)     - Returning cached data for key [efdd56@java.lang.String#fn] in cache [my.test.dao.UserDao@getUserByLoginId]

          看到上面的Log信息代替了原有的訪問數據庫的部分,高興了吧!

          好了,拍拍手,收工 ^_^!

          posted on 2009-01-17 09:30 都市淘沙者 閱讀(3206) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 宜都市| 景德镇市| 万载县| 二连浩特市| 明水县| 吉安县| 高台县| 清水河县| 兴安盟| 九寨沟县| 曲阳县| 聊城市| 龙门县| 合山市| 北流市| 丰县| 五常市| 宁晋县| 子洲县| 合山市| 修武县| 南安市| 西和县| 亚东县| 阿城市| 大连市| 彰化县| 洪雅县| 广平县| 灵宝市| 卓尼县| 双鸭山市| 镇远县| 耿马| 北安市| 咸宁市| 南郑县| 革吉县| 宜宾县| 白朗县| 林周县|