OpenSessionInViewFilter與org.springframework.dao.InvalidDataAccessApiUsageException


報錯:org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER/MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.

搞開發的時候碰到這個問題,在網上搜了一下原因,歸納成以下幾個知識點,部分摘抄網絡原文:

  1、延遲加載:

  Hibernate允許關聯對象進行延遲加載,前提是必須保證延遲加載操作是在同一個Session范圍之內進行。如果Service層返回了一個已啟用延遲加載的領域對象給View層,當View層訪問那些需要延遲加載的數據時,由於加載領域對象的Session已經關閉,將導致延遲加載數據的訪問異常(org.hibernate.LazyInitializationException)。

  2、OpenSessionInViewFilter:

  總所周知,Java類或者方法命名以長著稱,OpenSessionInViewFilter也是,顧名思義,它能夠讓我們在View層保持Session繼續Open。Spring提供的OpenSessionInViewFilter用來把一個Hibernate Session和一次完整的請求過程對應的線程相綁定(整個request過程都是用同一個Session,在請求結束后再解除綁定),允許在事務提交之后延遲加載View層需要的對象。在綁定過程中,它將自動被Spring的事務管理器探測到,所以,OpenSessionInViewFilter 適用於Service層使用HibernateTransactionManager或JtaTransactionManager進行事務管理的環境,也可以用於非事務只讀的數據操作中。

  “OpenSessionInViewFilter在getSession的時候,會把獲取回來的session的flush mode設為FlushMode.NEVER。然后把該sessionFactory綁定到 TransactionSynchronizationManager,使request的整個過程都使用同一個session,在請求過后再解除該sessionFactory的綁定,最后closeSessionIfNecessary根據該 session是否已和transaction綁定來決定是否關閉session。在這個過程中,若HibernateTemplate 發現自當前session有不是readOnly的transaction,就會獲取到FlushMode.AUTO Session,使方法擁有寫權限。”

  3、解決辦法:

  web.xml中配置OpenSessionInViewFilter初始參數singleSession:trueflushMode:AUTO

<filter>
  <filter-name>OpenSessionInViewFilter</filter-name>
  <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
  <init-param>
    <param-name>singleSession</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
    <param-name>flushMode</param-name>
    <param-value>AUTO</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>OpenSessionInViewFilter</filter-name>
  <url-pattern>*.action</url-pattern>
</filter-mapping>

  或者在Spring配置文件中,將方法的read-only設置為false。我用的是注解,為圖方便,將方法上的@Transactional(readOnly = true)去掉即可。

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

<aop:config>
  <aop:pointcut id="bussinessService" expression="execution(* com.fan.service.base.*.*(..))" />
  <aop:advisor pointcut-ref="bussinessService" advice-ref="txAdvice" />
</aop:config>

<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
    <tx:method name="get*" read-only="false" propagation="NOT_SUPPORTED"/>
    <tx:method name="find*" read-only="false" propagation="NOT_SUPPORTED"/>
    <tx:method name="save*" propagation="REQUIRED"/> 
    <tx:method name="update*" propagation="REQUIRED"/>
    <tx:method name="delete*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>

  4、備注:

  “盡 管Open Session In View看起來還不錯,其實副作用不少。看回上面OpenSessionInViewFilter的doFilterInternal方法代碼,這個方法 實際上是被父類的doFilter調用的,因此,我們可以大約了解的OpenSessionInViewFilter調用流程: request(請求)->open session並開始transaction->controller->View(Jsp)->結束transaction並close session。”
  “一切看起來很正確,尤其是在本地開發測試的時候沒出現問題,但試想下如果流程中的某一步被阻塞的話,那在這期間connection就一直被占用而不釋 放。最有可能被阻塞的就是在寫Jsp這步,一方面可能是頁面內容大,response.write的時間長,另一方面可能是網速慢,服務器與用戶間傳輸時 間久。當大量這樣的情況出現時,就有連接池連接不足,造成頁面假死現象。”
  “Open Session In View是個雙刃劍,放在公網上內容多流量大的網站請慎用。”


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM