學(xué)海拾遺

          生活、技術(shù)、思想無(wú)處不在學(xué)習(xí)
          posts - 52, comments - 23, trackbacks - 0, articles - 3
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          關(guān)于Spring和Hibernate整合后的懶加載和DAO模式應(yīng)用,尤其介紹了其中的Hibernate懶加載在應(yīng)用Spring的情況下在各個(gè)層次實(shí)現(xiàn)的情況。暫時(shí)是英文的,有時(shí)間試著翻譯一下!~

          Hibernate and Lazy Initialization

          Hibernate object relational mapping offers both lazy and non-lazy modes of object initialization. Non-lazy initialization retrieves an object and all of its related objects at load time. This can result in hundreds if not thousands of select statements when retrieving one entity. The problem is compounded when bi-directional relationships are used, often causing entire databases to be loaded during the initial request. Of course one could tediously examine each object relationship and manually remove those most costly, but in the end, we may be losing the ease of use benefit sought in using the ORM tool.

          The obvious solution is to employ the lazy loading mechanism provided by hibernate. This initialization strategy only loads an object's one-to-many and many-to-many relationships when these fields are accessed. The scenario is practically transparent to the developer and a minimum amount of database requests are made, resulting in major performance gains. One drawback to this technique is that lazy loading requires the Hibernate session to remain open while the data object is in use. This causes a major problem when trying to abstract the persistence layer via the Data Access Object pattern. In order to fully abstract the persistence mechanism, all database logic, including opening and closing sessions, must not be performed in the application layer. Most often, this logic is concealed behind the DAO implementation classes which implement interface stubs. The quick and dirty solution is to forget the DAO pattern and include database connection logic in the application layer. This works for small applications but in large systems this can prove to be a major design flaw, hindering application extensibility.

          Being Lazy in the Web Layer

          Fortunately for us, the Spring Framework has developed an out of box web solution for using the DAO pattern in combination with Hibernate lazy loading. For anyone not familiar with using the Spring Framework in combination with Hibernate, I will not go into the details here, but I encourage you to read Hibernate Data Access with the Spring Framework. In the case of a web application, Spring comes with both the OpenSessionInViewFilter and the OpenSessionInViewInterceptor. One can use either one interchangeably as both serve the same function. The only difference between the two is the interceptor runs within the Spring container and is configured within the web application context while the Filter runs in front of Spring and is configured within the web.xml. Regardless of which one is used, they both open the hibernate session during the request binding this session to the current thread. Once bound to the thread, the open hibernate session can transparently be used within the DAO implementation classes. The session will remain open for the view allowing lazy access to the database value objects. Once the view logic is complete, the hibernate session is closed either in the Filter doFilter method or the Interceptor postHandle method. Below is an example of the configuration of each component:

          Interceptor Configuration

           1 <beans>
           2   <bean id="urlMapping"     
           3      class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">    
           4        <property name="interceptors">
           5          <list>
           6               <ref bean="openSessionInViewInterceptor"/>
           7          </list>
           8        </property>
           9        <property name="mappings">
          10   
          11   </bean>
          12   
          13   <bean name="openSessionInViewInterceptor"  
          14     class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
          15        <property name="sessionFactory"><ref bean="sessionFactory"/></property>
          16   </bean>
          17 </beans>

          Filter Configuration

           1 <web-app>                           
           2   <filter>
           3     <filter-name>hibernateFilter</filter-name>
           4     <filter-class>
           5       org.springframework.orm.hibernate.support.OpenSessionInViewFilter
           6     </filter-class>
           7    </filter>
           8         
           9   <filter-mapping>
          10     <filter-name>hibernateFilter</filter-name>
          11      <url-pattern>*.spring</url-pattern>
          12   </filter-mapping>
          13   
          14 </web-app>            

          Implementing the Hibernate DAO's to use the open session is simple. In fact, if you are already using the Spring Framework to implement your Hibernate DAO's, most likely you will not have to change a thing. The DAO's must access Hibernate through the convenient HibernateTemplate utility, which makes database access a piece of cake. Below is an example DAO.

          Example DAO

           1  public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {
           2                                 
           3        public Product getProduct(Integer productId) {
           4               return (Product)getHibernateTemplate().load(Product.class, productId);
           5        }
           6 
           7        public Integer saveProduct(Product product) {
           8               return (Integer) getHibernateTemplate().save(product);
           9        }       
          10 
          11        public void updateProduct(Product product) {
          12               getHibernateTemplate().update(product);
          13        }
          14  }               

          Being Lazy in the Business Layer

          Even outside the view, the Spring Framework makes it easy to use lazy load initialization, through the AOP interceptor HibernateInterceptor. The hibernate interceptor transparently intercepts calls to any business object configured in the Spring application context, opening a hibernate session before the call, and closing the session afterward. Let's run through a quick example. Suppose we have an interface BusinessObject:

          1 public interface BusinessObject {
          2 
          3     public void doSomethingThatInvolvesDaos(); 
          4 }             

          The class BusinessObjectImpl implements BusinessObject:

          1 public class BusinessObjectImpl implements BusinessObject {
          2 
          3     public void doSomethingThatInvolvesDaos() {
          4         // lots of logic that calls
          5         // DAO classes Which access 
          6         // data objects lazily
          7     }
          8 }              

          Through some configurations in the Spring application context, we can instruct the HibernateInterceptor to intercept calls to the BusinessObjectImpl allowing it's methods to lazily access data objects. Take a look at the fragment below:

           1 <beans>
           2     <bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
           3          <property name="sessionFactory">
           4            <ref bean="sessionFactory"/>
           5          </property>
           6     </bean>
           7     <bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
           8        <property name="someDAO"><ref bean="someDAO"/></property>
           9     </bean>
          10     <bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
          11          <property name="target"><ref bean="businessObjectTarget"/></property>
          12          <property name="proxyInterfaces">
          13            <value>com.acompany.BusinessObject</value>
          14          </property>
          15          <property name="interceptorNames">
          16            <list>
          17               <value>hibernateInterceptor</value>
          18            </list>
          19          </property>
          20      </bean>            
          21 </beans>               

          When the businessObject bean is referenced, the HibernateInterceptor opens a hibernate session and passes the call onto the BusinessObjectImpl. When the BusinessObjectImpl has finished executing, the HibernateInterceptor transparently closes the session. The application code has no knowledge of any persistence logic, yet it is still able to lazily access data objects.

          Being Lazy in your Unit Tests

          Last but not least, we'll need the ability to test our lazy application from J-Unit. This is easily done by overriding the setUp and tearDown methods of the TestCase class. I prefer to keep this code in a convenient abstract TestCase class for all of my tests to extend.

           1 public abstract class MyLazyTestCase extends TestCase {
           2 
           3         public void setUp() throws Exception {
           4                 super.setUp();
           5                 SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
           6                 Session s = sessionFactory.openSession();
           7                 TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
           8 
           9         }
          10 
          11         protected Object getBean(String beanName) {
          12             //Code to get objects from Spring application context
          13         }
          14 
          15         public void tearDown() throws Exception {
          16                 super.tearDown();
          17                 SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.
          18                                getResource(sessionFactory);
          19                Session s = holder.getSession(); 
          20                s.flush();
          21                TransactionSynchronizationManager.unbindResource(sessionFactory);
          22                SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
          23        }
          24}
          Traceback: http://jroller.com/kbaum/entry/orm_lazy_initialization_with_dao

          在這里我試著翻譯一下,有部分地方改變了一下,希望看到的朋友多多指正。

          Hibernate和延遲裝載

          Hibernate ORM(關(guān)系-對(duì)象映射)針對(duì)對(duì)象的初始化分別提供了延遲和非延遲模式。非延遲裝載將在讀取時(shí)獲得對(duì)象和其所關(guān)聯(lián)的所有對(duì)象,這樣當(dāng)獲取一個(gè)實(shí)體對(duì)象時(shí),將會(huì)導(dǎo)致成千上萬(wàn)的select語(yǔ)句產(chǎn)生。這個(gè)問(wèn)題在使用雙向關(guān)聯(lián)的時(shí)候?qū)⒏踊靵y,經(jīng)常會(huì)導(dǎo)致在初始化請(qǐng)求就需要讀取整個(gè)數(shù)據(jù)庫(kù)。當(dāng)然,某個(gè)人可以去繁重地檢查每一個(gè)對(duì)象之間的關(guān)系,并且花費(fèi)更多的代價(jià)去手動(dòng)地移除它們。但是最終,我們可能已經(jīng)失去了使用ORM工具換來(lái)的易用性。

          因此,顯而易見(jiàn)的解決方案是使用Hibernate提供的延遲加載機(jī)制。這種初始化策略僅在當(dāng)那些字段訪問(wèn)時(shí)讀取這個(gè)對(duì)象的一對(duì)多和多對(duì)多關(guān)系。這種情況對(duì)于開(kāi)發(fā)者實(shí)際上是透明的,并且只創(chuàng)建一小部分?jǐn)?shù)量的數(shù)據(jù)庫(kù)請(qǐng)求,這樣就可以很大地提升性能。這種技術(shù)的一個(gè)缺點(diǎn)就是當(dāng)這個(gè)數(shù)據(jù)對(duì)象在使用時(shí),延遲加載就需要Hibernate session一直打開(kāi)。這就導(dǎo)致了一個(gè)主要問(wèn)題:當(dāng)嘗試通過(guò)DAO(數(shù)據(jù)訪問(wèn)對(duì)象)模式來(lái)抽象化持久層時(shí),為了完整地抽象持久化機(jī)制,所有的數(shù)據(jù)庫(kù)邏輯,包括打開(kāi)和關(guān)閉session,都將不會(huì)被應(yīng)用層調(diào)用執(zhí)行。最經(jīng)常的情況,這些邏輯被后面的實(shí)現(xiàn)某些DAO接口的類(lèi)來(lái)關(guān)注。但是,這個(gè)快速但不好的解決方案違反了DAO模式,并且在應(yīng)用層包含了數(shù)據(jù)庫(kù)連接邏輯。這樣做對(duì)于小型應(yīng)用可能適合,但是在大型系統(tǒng)中,這會(huì)產(chǎn)生一個(gè)重大的設(shè)計(jì)失誤,阻止了應(yīng)用的擴(kuò)展。

          在WEB層上延遲

          我們是幸運(yùn)的,Spring框架開(kāi)發(fā)了一個(gè)開(kāi)箱即用(out of box)的WEB解決方案,用來(lái)在組合Hibernate延遲加載時(shí)使用DAO模式。對(duì)于不熟悉使用Spring如何組合Hibernate的人,在這里我不會(huì)進(jìn)入詳細(xì)的細(xì)節(jié),但是我鼓勵(lì)你去閱讀使用Spring框架進(jìn)行Hibernate數(shù)據(jù)訪問(wèn)Hibernate Data Access with the Spring Framework》。就一個(gè)WEB應(yīng)用來(lái)說(shuō),Spring提供了OpenSessionInViewFilterOpenSessionInViewInterceptor,一個(gè)可替換另一個(gè)使用而得到同樣的功能。兩者的唯一區(qū)別就是攔截器(Interceptor)在Spring容器中運(yùn)行并且在WEB應(yīng)用程序上下文(Application Context)中被配置,而過(guò)濾器(Filter)運(yùn)行Spring之前并且是在web.xml中配置的。不管使用哪一個(gè),它們都在綁定了session到當(dāng)前線程的請(qǐng)求中打開(kāi)Hibernate Session。一旦綁定到了線程,打開(kāi)的Hibernate Session就能夠透明地在DAO實(shí)現(xiàn)類(lèi)中使用了。這個(gè)Session將一直打開(kāi),為視圖(view)提供對(duì)數(shù)據(jù)庫(kù)值對(duì)象的延遲訪問(wèn)。視圖邏輯如果完成了,Hibernate Session就會(huì)在過(guò)濾器的doFilter或攔截器的postHandle方法中關(guān)閉。下面是各個(gè)部件配置的一個(gè)例子:

          攔截器配置

           1 <beans>
           2   <bean id="urlMapping"     
           3      class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">    
           4        <property name="interceptors">
           5          <list>
           6               <ref bean="openSessionInViewInterceptor"/>
           7          </list>
           8        </property>
           9        <property name="mappings">
          10   
          11   </bean>
          12   
          13   <bean name="openSessionInViewInterceptor"  
          14     class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
          15        <property name="sessionFactory"><ref bean="sessionFactory"/></property>
          16   </bean>
          17 </beans>

          過(guò)濾器配置

           1 <web-app>                           
           2   <filter>
           3     <filter-name>hibernateFilter</filter-name>
           4     <filter-class>
           5       org.springframework.orm.hibernate.support.OpenSessionInViewFilter
           6     </filter-class>
           7    </filter>
           8         
           9   <filter-mapping>
          10     <filter-name>hibernateFilter</filter-name>
          11      <url-pattern>*.spring</url-pattern>
          12   </filter-mapping>
          13   
          14 </web-app>            

          實(shí)現(xiàn)Hibernate DAO來(lái)使用打開(kāi)的Session很簡(jiǎn)單。實(shí)際上,如果你準(zhǔn)備用Spring框架來(lái)實(shí)現(xiàn)你的Hibernate DAO,最可能的是你不需要改變?nèi)魏我惶幋a。強(qiáng)制DAO必須通過(guò)方便的HibernateTemplate工具來(lái)訪問(wèn)Hibernate,將會(huì)使得數(shù)據(jù)庫(kù)訪問(wèn)是小菜一碟。下面是一個(gè)DAO的例子。

          DAO例子

           1  public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {
           2                                 
           3        public Product getProduct(Integer productId) {
           4               return (Product)getHibernateTemplate().load(Product.class, productId);
           5        }
           6 
           7        public Integer saveProduct(Product product) {
           8               return (Integer) getHibernateTemplate().save(product);
           9        }       
          10 
          11        public void updateProduct(Product product) {
          12               getHibernateTemplate().update(product);
          13        }
          14  }               

          在業(yè)務(wù)層延遲

          即使是在視圖外面,Spring框架通過(guò)AOP攔截器HibernateInterceptor,也讓延遲加載初始化變得很容易使用。這個(gè)Hibernate攔截器透明地?cái)r截在Spring應(yīng)用程序上下文(Application Context)中配置的任何業(yè)務(wù)對(duì)象的調(diào)用,在調(diào)用前打開(kāi)一個(gè)Hibernate Session,同時(shí)在調(diào)用后關(guān)閉Session。讓我們通過(guò)一個(gè)快速的例子來(lái)運(yùn)行一下。假設(shè)我們有一個(gè)BusinessObject接口:

          1 public interface BusinessObject {
          2 
          3     public void doSomethingThatInvolvesDaos(); 
          4 }             

          BusinessObjectImpl類(lèi)實(shí)現(xiàn)了BusinessObject接口:

          1 public class BusinessObjectImpl implements BusinessObject {
          2 
          3     public void doSomethingThatInvolvesDaos() {
          4         // lots of logic that calls
          5         // DAO classes Which access 
          6         // data objects lazily
          7     }
          8 }              

          通過(guò)在Spring應(yīng)用程序上下文(Application Context)中的一些配置,我們能構(gòu)造HibernateInterceptor來(lái)攔截BusinessObjectImpl的調(diào)用,以允許它的方法延遲訪問(wèn)數(shù)據(jù)對(duì)象。看一看下面的代碼片斷:

           1 <beans>
           2     <bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
           3          <property name="sessionFactory">
           4            <ref bean="sessionFactory"/>
           5          </property>
           6     </bean>
           7     <bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
           8        <property name="someDAO"><ref bean="someDAO"/></property>
           9     </bean>
          10     <bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
          11          <property name="target"><ref bean="businessObjectTarget"/></property>
          12          <property name="proxyInterfaces">
          13            <value>com.acompany.BusinessObject</value>
          14          </property>
          15          <property name="interceptorNames">
          16            <list>
          17               <value>hibernateInterceptor</value>
          18            </list>
          19          </property>
          20      </bean>            
          21 </beans>               

          當(dāng)businessObject bean被引用時(shí),HibernateInterceptor打開(kāi)一個(gè)Hibernate Session并且將調(diào)用傳遞給BusinessObjectImpl。當(dāng)BusinessObjectImpl完成執(zhí)行時(shí),HibernateInterceptor透明地關(guān)閉這個(gè)Session。應(yīng)用程序代碼沒(méi)有任何的持久化邏輯,然而它也能夠延遲地訪問(wèn)數(shù)據(jù)對(duì)象。

          在你的單元測(cè)試中延遲

          最后一項(xiàng)重點(diǎn)是,我們需要能夠從Junit中測(cè)試我們的延遲應(yīng)用的能力。這很容易通過(guò)覆蓋用例測(cè)試(TestCase)類(lèi)中的setUp和tearDown方法做到。我更趨向于將這些代碼放于一個(gè)方便的能被我所有的測(cè)試?yán)^承的抽象測(cè)試用例(TestCase)類(lèi)中。

           

           1 public abstract class MyLazyTestCase extends TestCase {
           2 
           3         public void setUp() throws Exception {
           4                 super.setUp();
           5                 SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
           6                 Session s = sessionFactory.openSession();
           7                 TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
           8 
           9         }
          10 
          11         protected Object getBean(String beanName) {
          12             //Code to get objects from Spring application context
          13         }
          14 
          15         public void tearDown() throws Exception {
          16                 super.tearDown();
          17                 SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.
          18                                getResource(sessionFactory);
          19                Session s = holder.getSession(); 
          20                s.flush();
          21                TransactionSynchronizationManager.unbindResource(sessionFactory);
          22                SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
          23        }
          24}

          主站蜘蛛池模板: 翼城县| 车险| 舒兰市| 临湘市| 棋牌| 凤城市| 汕尾市| 固始县| 荣昌县| 莆田市| 莲花县| 乌拉特中旗| 紫阳县| 简阳市| 马龙县| 汉川市| 张家川| 都匀市| 新巴尔虎左旗| 定边县| 临汾市| 白玉县| 正定县| 宁波市| 稻城县| 略阳县| 安国市| 涡阳县| 临澧县| 来宾市| 临桂县| 禄丰县| 剑河县| 和平县| 醴陵市| 安吉县| 法库县| 沈阳市| 桐乡市| 伊宁市| 普兰县|