posts - 495,  comments - 11,  trackbacks - 0

          6.5.4 使用HibernateCallBack

          HibernateTemplate還提供了一種更加靈活的方式來操作數據庫,通過這種方式可以完全使用Hibernate的操作方式。HibernateTemplate的靈活訪問方式可通過如下兩個方法完成:

          ?? ● Object execute(HibernateCallback action)。

          ?? ● List execute(HibernateCallback action)。

          這兩個方法都需要一個HibernateCallback的實例,HibernateCallback實例可在任何有效的Hibernate數據訪問中使用。程序開發者通過HibernateCallback,可以完全使用Hibernate靈活的方式來訪問數據庫,解決Spring封裝Hibernate后靈活性不足的缺陷。

          HibernateCallback是一個接口,該接口包含一個方法doInHibernate(org.hibernate. Session session),該方法只有一個參數Session。在開發中提供HibernateCallback實現類時,必須實現接口里包含的doInHibernate方法,在該方法體內即可獲得Hibernate Session的引用,一旦獲得了Hibernate Session的引用,就可以完全以Hibernate的方式進行數據庫訪問。

          注意:doInHibernate方法內可以訪問Session,該Session對象是綁定在該線程的Session實例。該方法內的持久層操作,與不使用Spring時的持久層操作完全相同。這保證了對于復雜的持久層訪問,依然可以使用Hibernate的訪問方式。

          下面的代碼對HibernateDaoSupport類進行擴展(雖然Spring 2.0的HibernateTemplate提供了一個分頁方法setMaxResults,但僅此一個方法依然不能實現分頁查詢),這種擴展主要是為該類增加了3個分頁查詢的方法,分頁查詢時必須直接調用Hibernate的Session完成,因此,必須借助于HibernateCallBack的幫助。

          public class YeekuHibernateDaoSupport extends HibernateDaoSupport

          {

          ??? /**

          ??? * 使用hql 語句進行分頁查詢操作

          ??? * @param hql 需要查詢的hql語句

          ??? * @param offset 第一條記錄索引

          ??? * @param pageSize 每頁需要顯示的記錄數

          ??? * @return 當前頁的所有記錄

          ??? */

          ??? public List findByPage(final String hql,

          ??????? final int offset, final int pageSize)

          ??? {

          ??????? //HibernateDaoSupport已經包含了getHibernateTemplate()方法

          ??????? List list = getHibernateTemplate().executeFind(new
          ??????? HibernateCallback()

          ??????????? {

          ??????????????? public Object doInHibernate(Session session)

          ??????????????????? throws HibernateException, SQLException

          ??????????????? //該方法體內以Hibernate方法進行持久層訪問

          ??????????????? {

          ??????????????????? List result = session.createQuery(hql)

          ??????????????????????????????????????? .setFirstResult(offset)

          ??????????????????????? ???????????????? .setMaxResults(pageSize)

          ??????????????????????????????????????? .list();

          ??????????????????? return result;

          ??????????????? }

          ??????????? });

          ??????? return list;

          ??? }

          ??? /**

          ??? * 使用hql 語句進行分頁查詢操作

          ??? * @param hql 需要查詢的hql語句

          ??? * @param value 如果hql有一個參數需要傳入,value就是傳入的參數

          ??? * @param offset 第一條記錄索引

          ??? * @param pageSize 每頁需要顯示的記錄數

          ??? * @return 當前頁的所有記錄

          ??? */

          ??? public List findByPage(final String hql , final Object value ,

          ??????? final int offset, final int pageSize)

          ??? {

          ??????? List list = getHibernateTemplate().executeFind(new
          ??????? HibernateCallback()

          ??????????? {

          ??????????????? public Object doInHibernate(Session session)

          ??????? ??????????? throws HibernateException, SQLException

          ??????????????? {

          ??????????????????? //下面查詢的是最簡單的Hiberante HQL查詢

          ??????????????????? List result = session.createQuery(hql)

          ??????????????????????????????? ???????? .setParameter(0, value)

          ??????????????????????????????????????? .setFirstResult(offset)

          ??????????????????????? ???????????????? .setMaxResults(pageSize)

          ??????????????????? ??????????????????? .list();

          ??????????????????? return result;

          ??????????????? }

          ??????????? });

          ??????? return list;

          ??? }

          ??? /**

          ??? * 使用hql 語句進行分頁查詢操作

          ??? * @param hql 需要查詢的hql語句

          ??? * @param values 如果hql有多個參數需要傳入,values就是傳入的參數數組

          ??? * @param offset 第一條記錄索引

          ??? * @param pageSize 每頁需要顯示的記錄數

          ??? * @return 當前頁的所有記錄

          ??? */

          ??? public List findByPage(final String hql, final Object[] values,

          ??????? final int offset, final int pageSize)

          ??? {

          ??????? List list = getHibernateTemplate().executeFind(new
          ??????? HibernateCallback()

          ??????????? {

          ??????????????? public Object doInHibernate(Session session)

          ??????????????????? throws HibernateException, SQLException

          ??????????????? {

          ??????????????????? Query query = session.createQuery(hql);

          ??????????????????? for (int i = 0 ; i < values.length ; i++)

          ??????????????????? {

          ??????????????????????? query.setParameter( i, values[i]);

          ??????????????????? }

          ??????????????????? List result = query.setFirstResult(offset)

          ??????????????????????? ?????????????? .setMaxResults(pageSize)

          ??????????????????????????????????? ?? .list();

          ??????????????????? return result;

          ??????????????? }

          ??????????? });

          ??????? return list;

          ??? }

          }

          在上面的代碼實現中,直接使用了getHibernateTemplate()方法,這個方法由Hibernate- DaoSupport提供。而YeekuHibernateDaoSupport是HibernateDaoSupport的子類,因此,可以直接使用該方法。

          當實現doInHibernate(Session session)方法時,完全以Hibernate的方式進行數據庫訪問,這樣保證了Hibernate進行數據庫訪問的靈活性。

          注意:Spring提供的XxxTemplate和XxxCallBack互為補充,二者體現了Spring框架設計的用心良苦:XxxTemplate對通用操作進行封裝,而XxxCallBack解決了封裝后靈活性不足的缺陷。

          6.5.5 實現DAO組件

          為了實現DAO組件,Spring提供了大量的XxxDaoSupport類,這些DAO支持類對于實現DAO組件大有幫助,因為這些DAO支持類已經完成了大量基礎性工作。

          Spring為Hibernate的DAO提供了工具類HibernateDaoSupport。該類主要提供如下兩個方法以方便DAO的實現:

          ?? ● public final HibernateTemplate getHibernateTemplate()。

          ?? ● public final void setSessionFactory(SessionFactory sessionFactory)。

          其中,setSessionFactory方法可用于接收Spring的ApplicationContext的依賴注入,可接收配置在Spring的SessionFactory實例,getHibernateTemplate方法用于返回通過SessionFactory產生的HibernateTemplate實例,持久層訪問依然通過HibernateTemplate實例完成。

          下面實現的DAO組件繼承了Spring提供的HibernateDaoSupport類,依然實現了PersonDao接口,其功能與前面提供的PersonDao實現類完全相同。其代碼如下:

          public class PersonDaoHibernate extends HibernateDaoSupport implements PersonDao

          {

          ??? /**

          ???? * 加載人實例

          ???? * @param id 需要加載的Person實例的主鍵值

          ???? * @return 返回加載的Person實例

          ???? */

          ??? public Person get(int id)

          ??? {

          ??????? return (Person)getHibernateTemplate().get(Person.class, new
          ??????? Integer(id));

          ??? }

          ??? /**

          ???? * 保存人實例

          ???? * @param person 需要保存的Person實例

          ???? */???

          ??? public void save(Person person)

          ??? {

          ??????? getHibernateTemplate().save(person);

          ??? }

          ??? /**

          ???? * 修改Person實例

          ???? * @param person 需要修改的Person實例

          ???? */

          ??? public void update(Person person)

          ??? {

          ??????? getHibernateTemplate().update(person);

          ??? }

          ??? /**

          ???? * 刪除Person實例

          ???? * @param id 需要刪除的Person的id

          ???? */

          ??? public void delete(int id)

          ??? {

          ??????? getHibernateTemplate().delete(getHibernateTemplate().
          ??????? get(Person.class, new Integer(id)));

          ??? }

          ??? /**

          ???? * 刪除Person實例

          ???? * @param person 需要刪除的Person實例

          ???? */

          ??? public void delete(Person person)

          ??? {

          ??????? getHibernateTemplate().delete(person);

          ??? }

          ??? /**

          ???? * 根據用戶名查找Person

          ???? * @param name 用戶名

          ???? * @return 用戶名對應的全部用戶

          ???? */

          ??? public List findByPerson(String name)

          ??? {

          ??????? return getHibernateTemplate().find("from Person p where p.name
          ??????? like ?" , name);???????

          ??? }

          ??? /**

          ??? * 返回全部的Person實例

          ??? * @return 全部的Person實例

          ??? */

          ??? public List findAllPerson()

          ??? {

          ??????? return getHibernateTemplate().find("from Person ");

          ??? }

          }

          上面的代碼與前面的PersonDAOImpl對比會發現,代碼量大大減少。事實上,DAO的實現依然借助于HibernateTemplate的模板訪問方式,只是HibernateDaoSupport將依賴注入SessionFactory的工作已經完成,獲取HibernateTemplate的工作也已完成。該DAO的配置必須依賴于SessionFactory,配置文件與前面部署DAO組件的方式完全相同,此處不再贅述。

          在繼承HibernateDaoSupport的DAO實現里,Hibernate Session的管理完全不需要打開代碼,而由Spring來管理。Spring會根據實際的操作,采用“每次事務打開一次session”的策略,自動提高數據庫訪問的性能。

          6.5.6 使用IoC容器組裝各種組件

          至此為止,J2EE應用所需要的各種組件都已經出現了,從MVC層的控制器組件,到業務邏輯組件,以及持久層的DAO組件,已經全部成功實現。應用程序代碼并未將這些組件耦合在一起,代碼中都是面向接口編程,因此必須利用Spring的IoC容器將他們組合在一起。

          從用戶角度來看,用戶發出HTTP請求,當MVC框架的控制器組件攔截到用戶請求時,將調用系統的業務邏輯組件,而業務邏輯組件則調用系統的DAO組件,而DAO組件則依賴于SessionFactory和DataSource等底層組件實現數據庫訪問。

          從系統實現角度來看,IoC容器先創建SessionFactory和DataSource等底層組件,然后將這些底層組件注入給DAO組件,提供一個完整的DAO組件,并將此DAO組件注入給業務邏輯組件,從而提供一個完整的業務邏輯組件,而業務邏輯組件又被注入給控制器組件,控制器組件負責攔截用戶請求,并將處理結果呈現給用戶——這一系列的銜接都由Spring的IoC容器提供實現。

          下面給出關于如何在容器中配置J2EE組件的大致模板,其模板代碼如下:

          <?xml version="1.0" encoding="GBK"?>

          <!-- beans是Spring配置文件的根元素,并且指定了Schema信息 -->

          <beans xmlns="http://www.springframework.org/schema/beans"

          ?????? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          ?????? xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

          ??? <!-- 定義數據源Bean,使用C3P0數據源實現 -->

          ??? <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
          ??? destroy-method="close">

          ??????? <!-- 指定連接數據庫的驅動 -->

          ??????? <property name="driverClass" value="com.mysql.jdbc.Driver"/>

          ??????? <!-- 指定連接數據庫的URL -->

          ??????? <property name="jdbcUrl" value="jdbc:mysql://localhost/j2ee"/>

          ??????? <!-- 指定連接數據庫的用戶名 -->

          ??????? <property name="user" value="root"/>

          ??????? <!-- 指定連接數據庫的密碼 -->

          ??????? <property name="password" value="32147"/>

          ??????? <!-- 指定連接數據庫連接池的最大連接數 -->

          ??????? <property name="maxPoolSize" value="40"/>

          ??????? <!-- 指定連接數據庫連接池的最小連接數 -->

          ??????? <property name="minPoolSize" value="1"/>

          ??????? <!-- 指定連接數據庫連接池的初始化連接數 -->

          ??????? <property name="initialPoolSize" value="1"/>

          ??????? <!-- 指定連接數據庫連接池的連接最大空閑時間 -->

          ??????? <property name="maxIdleTime" value="20"/>

          ??? </bean>

          ??? <!-- 定義Hibernate的SessionFactory Bean -->

          ??? <bean id="sessionFactory" class="org.springframework.orm.hibernate3.
          ??? LocalSessionFactoryBean">

          ??????? <!-- 依賴注入數據源,注入的正是上文中定義的dataSource -->

          ??????? <property name="dataSource" ref="dataSource"/>

          ??????? <!-- mappingResources屬性用來列出全部映射文件 -->

          ??????? <property name="mappingResources">

          ??????????? <list>

          ??????????????? <!-- 以下用來列出所有的PO映射文件 -->

          ??????????????? <value>lee/Person.hbm.xml</value>

          ??????????????? <!-- 此處還可列出更多的PO映射文件 -->

          ??????????? </list>

          ??????? </property>

          ????????? <!-- 定義Hibernate的SessionFactory屬性 -->

          ??????? <property name="hibernateProperties">

          ??????? ???? <props>

          ??????????????? <!-- 指定Hibernate的連接方言 -->

          ??????????? ??? <prop key="hibernate.dialect">org.hibernate.dialect.
          ??????????????? MySQLDialect</prop>

          ??????????????? <!-- 指定啟動應用時,是否根據Hibernate映射文件創建數據表 -->

          ??????????? ????? <prop key="hibernate.hbm2ddl.auto">update</prop>

          ??????? ???? </props>

          ??????? </property>

          ??? </bean>

          ??? <!-- 配置Person持久化類的DAO Bean -->

          ??? <bean id="personDao" class="lee.PersonDaoImpl">

          ??????? <!-- 采用依賴注入來傳入SessionFactory的引用 -->

          ??????? <property name="sessionFactory" ref="sessionFactory"/>

          ??? </bean>

          ??? <!-- 下面能以相同的方式配置更多的持久化Bean -->

          ??? ...

          ??? <bean id="myService" class="lee.MyServiceImp">

          ??????? <!-- 注入業務邏輯組件所必需的DAO組件 -->

          ??? ??? <property name="peronDdao" ref=" personDao "/>

          ??????? <!-- 此處可采用依賴注入更多的DAO組件 -->

          ??????? ...

          ??? </bean>

          ??? <!-- 配置控制器Bean,設置起作用域為Request -->

          ??? <bean name="/login" class="lee.LoginAction" scope="request">

          ??????? <!-- 依賴注入控制器所必需的業務邏輯組件 -->

          ??????? <property name="myService" ref=" myService "/>

          ??? </bean>

          </beans>

          在上面的配置文件中,同時配置了控制器Bean、業務邏輯組件Bean、DAO組件Bean以及一些基礎資源Bean。各組件的組織被解耦到配置文件中,而不是在代碼層次的低級耦合。

          當客戶端的HTTP請求向/login.do發送請求時,將被容器中的lee.LoginAction攔截,LoginAction調用myService Bean,myService Bean則調用personDao等系列DAO組件,整個流程將系統中的各組件有機地組織在一起。

          注意:在實際應用中,很少會將DAO組件、業務邏輯組件以及控制組件都配置在同一個文件中。而是在不同配置文件中,配置相同一組J2EE應用組件。

          6.5.7 使用聲明式事務

          在上面的配置文件中,部署了控制器組件、業務邏輯組件、DAO組件,幾乎可以形成一個完整的J2EE應用。但有一個小小的問題:事務控制。系統沒有任何事務邏輯,沒有事務邏輯的應用是不可想象的。

          Spring提供了非常簡潔的聲明式事務控制,只需要在配置文件中增加事務控制片段,業務邏輯代碼無須任何改變。Spring的聲明式事務邏輯,甚至支持在不同事務策略之間切換。

          配置Spring聲明式事務時,通常推薦使用BeanNameAutoProxyCreator自動創建事務代理。通過這種自動事務代理的配置策略,增加業務邏輯組件,只需要在BeanNameAutoProxyCreator Bean配置中增加一行即可,從而避免了增量式配置。

          在上面的配置模板文件中增加如下配置片段,系統的myService業務邏輯組件將變成事務代理Bean,從而為業務邏輯方法增加事務邏輯。

          ??? <!-- 配置Hibernate的局部事務管理器 -->

          ??? <!-- 使用HibernateTransactionManager類,該類是PlatformTransactionManager
          ??? 接口,針對采用Hibernate持久化連接的特定實現 -->

          ??? <bean id="transactionManager"

          ????? ??? class="org.springframework.orm.hibernate3.
          ???????? HibernateTransactionManager">

          ??????? <!-- HibernateTransactionManager Bean需要依賴注入一個SessionFactory
          ??????? bean的引用 -->

          ??? ???? <property name="sessionFactory" ref="sessionFactory"/>

          ??? </bean>

          ??? <!-- 配置事務攔截器Bean -->

          ??? <bean id="transactionInterceptor"

          ??????? class="org.springframework.transaction.interceptor.
          ??????? TransactionInterceptor">

          ??????? <!-- 事務攔截器bean需要依賴注入一個事務管理器 -->

          ??????? <property name="transactionManager" ref="transactionManager"/>

          ??????? <property name="transactionAttributes">

          ??????????? <!-- 下面定義事務傳播屬性 -->

          ??????????? <props>

          ??????????????? <prop key="insert*">PROPAGATION_REQUIRED</prop>

          ??????????????? <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

          ??????????????? <prop key="*">PROPAGATION_REQUIRED</prop>

          ??????????? </props>

          ??????? </property>

          ??? </bean>

          ??? <!-- 定義BeanNameAutoProxyCreator的Bean后處理器 -->

          ??? <bean class="org.springframework.aop.framework.autoproxy.
          ??? BeanNameAutoProxyCreator">

          ??? <!-- 指定對滿足哪些bean name的bean自動生成業務代理 -->

          ??? ??? <property name="beanNames">

          ??????????? <!-- 下面是所有需要自動創建事務代理的Bean -->

          ??????????? <list>

          ??????????????? <value>myService</value>

          ??????????????? <!-- 下面還可增加需要增加事務邏輯的業務邏輯Bean -->

          ??????????????? ...

          ??????????? </list>

          ??????????? <!-- 此處可增加其他需要自動創建事務代理的Bean -->

          ??? ??? </property>

          ??????? <!-- 下面定義BeanNameAutoProxyCreator所需的攔截器 -->

          ??????? <property name="interceptorNames">

          ??????????? <list>

          ??????????????? <value>transactionInterceptor</value>

          ??????????????? <!-- 此處可增加其他新的Interceptor -->

          ??????????? </list>

          ??????? </property>

          ??? </bean>

          一旦增加了如上的配置片段,系統中的業務邏輯方法就有了事務邏輯。這種聲明式事務配置方式可以在不同的事務策略之間自由切換。

          提示:盡量使用聲明式事務配置方式,而不要在代碼中完成事務邏輯。

          posted on 2009-07-19 10:24 jadmin 閱讀(379) 評論(0)  編輯  收藏

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


          網站導航:
           
          主站蜘蛛池模板: 巴林右旗| 安陆市| 蓬溪县| 绵阳市| 延津县| 女性| 上饶县| 会宁县| 甘谷县| 贡觉县| 康平县| 天津市| 正蓝旗| 谢通门县| 乌鲁木齐县| 常熟市| 广饶县| 龙江县| 介休市| 河津市| 边坝县| 醴陵市| 历史| 崇阳县| 临安市| 铜川市| 德惠市| 龙门县| 德化县| 环江| 从化市| 阜城县| 樟树市| 黑水县| 讷河市| 井陉县| 陈巴尔虎旗| 宝鸡市| 眉山市| 佛冈县| 乳山市|