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.web

搞開發的時候碰到這個問題,在網上搜了一下緣由,概括成如下幾個知識點,部分摘抄網絡原文:spring

  一、延遲加載:express

  Hibernate容許關聯對象進行延遲加載,前提是必須保證延遲加載操做是在同一個Session範圍以內進行。若是Service層返回了 一個已啓用延遲加載的領域對象給View層,當View層訪問那些須要延遲加載的數據時,因爲加載領域對象的Session已經關閉,將致使延遲加載數據 的訪問異常(org.hibernate.LazyInitializationException)。服務器

  二、OpenSessionInViewFilter:網絡

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

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

  三、解決辦法:測試

  web.xml中配置OpenSessionInViewFilter初始參數singleSession:true、flushMode: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)去掉便可。url

 

<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>

 

四、備註:

  「盡 管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是個雙刃劍,放在公網上內容多流量大的網站請慎用。」

相關文章
相關標籤/搜索