天天看點

spring 的OpenSessionInViewFilter簡介

假設在你的應用中hibernate是通過spring 來管理它的session.如果在你的應用中沒有使用opensessioninviewfilter或者opensessioninviewinterceptor。session會在transaction結束後關閉。

如果你采用了spring的聲明式事務模式,它會對你的被代理對象的每一個方法進行事務包裝(aop的方式)。如下:

 <bean id="txproxytemplate" abstract="true"

        class="org.springframework.transaction.interceptor.transactionproxyfactorybean">

        <property name="transactionmanager" ref="transactionmanager"/>

        <property name="transactionattributes">

            <props>

                <prop key="save*">propagation_required</prop>

                <prop key="remove*">propagation_required</prop>

                <prop key="*">propagation_required,readonly</prop>

            </props>

        </property>

    </bean>

    <bean id="manager" parent="txproxytemplate">

        <property name="target">

            <bean class="org.appfuse.service.impl.basemanager">

                <property name="dao" ref="dao"/>

            </bean>

目标類org.appfuse.service.impl.basemanager 的  save *方法的事務類型propagation_required  ,remove* 方法的事務類型propagation_required

其他的方法的事務類型是propagation_required,readonly。

是以給你的感覺是調用這個名為“manager”的bean的方法之後session就關掉了。

如果應用中使用了opensessioninviewfilter或者opensessioninviewinterceptor,所有打開的session會被儲存在一個線程變量裡。線上程退出前通過

opensessioninviewfilter或者opensessioninviewinterceptor斷開這些session。 為什麼這麼做?這主要是為了實作hibernate的延遲加載功能。基于一個請求

一個hibernate session的原則。

spring中對opensessioninviewfilter的描述如下:

它是一個servlet2.3過濾器,用來把一個hibernate session和一次完整的請求過程對應的線程相綁定。目的是為了實作"open session in view"的模式。

例如: 它允許在事務送出之後延遲加載顯示所需要的對象。

這個過濾器和 hibernateinterceptor 有點類似:它是通過線程實作的。無論是沒有事務的應用,還是有業務層事務的應用(通過hibernatetransactionmanager 或

jtatransactionmanager的方式實作)它都适用。在後一種情況下,事務會自動采用由這個filter綁定的session來進行相關的操作以及根據實際情況完成送出操作。

警告: 如果在你的應用中,一次請求的過程中使用了單一的一個hibernate session,在這種情況下,采用這個filter會産生一些以前沒遇到的問題。特别需要注意的是通過

hibernate session重新組織持久化對象之間關系的相關操作需要在請求的最開始進行。以免與已經加載的相同對象發生沖突。

或者,我們可以通過指定"singlesession"="false"的方式把這個過濾器調到延期關閉模式。這樣在一次請求的過程中不會使用一個單一的session.每一次資料通路或事務相關

操作都使用屬于它自己的session(有點像不使用open session in view).這些session都被注冊成延遲關閉模式,即使是在這一次的請求中它相關操作已經完成。

"一次請求一個session" 對于一級緩存而言很有效,但是這樣可以帶來副作用。例如在saveorupdate的時候或事物復原之後,雖然它和“no open session in view”同樣安全。

但是它卻允許延遲加載。

它會在spring的web應用的上下文根中查找session工廠。它也支援通過在web.xml中定義的“sessionfactorybeanname”的init-param元素 指定的session工廠對應的bean的

名字來查找session工廠。預設的bean的名字是"sessionfactory".他通過每一次請求查找一次sessionfactory的方式來避免由初始化順序引起的問題(當使用contextloaderservlet

來內建spring的時候 ,spring 的應用上下文是在這個filter 之後才被初始化的)。

預設的情況下,這個filter 不會同步hibernate session.這是因為它認為這項工作是通過業務層的事務來完成的。而且hibernateaccessors 的flushmode為flush_eager.如果你

想讓這個filter在請求完成以後同步session.你需要覆寫它的closesession方法,在這個方法中在調用父類的關閉session操作之前同步session.此外你需要覆寫它的getsession()

方法。傳回一個session它的flushmode 不是預設的flushmode.never。需要注意的是getsession()和closesession()方法隻有在single session的模式中才被調用。

在myfaces的wiki裡提供了opensessioninviewfilter的一個子類如下:

public class opensessioninviewfilter extends org.springframework.orm.hibernate3.support.opensessioninviewfilter {

        /**

         * we do a different flushmode than in the codebase

         * here

         */

        protected session getsession(sessionfactory sessionfactory) throws dataaccessresourcefailureexception {

                session session = sessionfactoryutils.getsession(sessionfactory, true);

                session.setflushmode(flushmode.commit);

                return session;

        }

         * we do an explicit flush here just in case

         * we do not have an automated flush

        protected void closesession(session session, sessionfactory factory) {

                session.flush();

                super.closesession(session, factory);

}