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