1、SSH整合以後事務問題和總結java
1.引入問題:DAO層測試git
假設將User對象設置爲懶加載模式,在dao層使用load方法。github
注意,註釋不要放開。web
使用以下的代碼塊進行測試:spring
會報錯:no session。sql
爲何會沒有session呢,由於在代碼apache
User user=(User) hibernateTemplate.load(User.class, id);
執行完成以後session就已經關閉了。緩存
2.Service層測試安全
(1)Service層代碼:服務器
DAO層代碼:
測試代碼:
運行結果:
出現的錯誤結果和上面徹底相同。
(2)解決錯誤
將DAO層中的代碼註釋去掉或者將Service中的代碼註釋去掉。
註釋的代碼做用是打印結果,只是一個System.out.println方法的執行;雖然如此,程序運行再也不有問題。
3.緣由分析
(1)爲何測試DAO不管怎麼測試都會報出異常?
由於DAO層調用完成getUserById方法以後session就立刻關閉了,因此執行下一句代碼的時候即便是打印輸出的代碼也會報錯。
(2)爲何測試Service的時候若是沒有去掉註釋的話,就會出錯?
由於去掉註釋的話,Service層調用完成getUserById方法以後Session就會關閉,效果和在DAO層是徹底相同的,因此結果和在DAO層是徹底相同的。報錯的信息徹底相同。
(3)爲何Service層去掉註釋以後就不報錯了?
Service層去掉註釋以後因爲方法尚未退出,因此會將數據保存到User對象中;因此數據已經保存到了內存中,這樣就不會報錯了。
(4)在DAO層去掉註釋以後會不會報錯?
去掉DAO層的註釋以後的運行結果:
結果是沒有報錯,爲何,DAO層調用完成查詢的方法以後不是會當即關閉session嗎?
結果是沒有關閉Session,緣由是在Service層調用的方法,該方法具備事務,並且該事務的範圍擴展到了DAO層。最根本的緣由是在Spring配置文件中的配置:
propagation屬性值是默認的,因此在Service層中方法執行的時候,執行的全部方法都將會帶有事務,直到方法結束,事務也會隨之結束,所以DAO層纔沒有當即結束事務(關閉session)。
4.總結SSH整合以後事務管理的範圍
(1)若是當前執行的方法沒有事務環境,當執行完成this.hibernateTemplate中的方法以後session當即關閉。
(2)若是當前執行的方法有事務環境,當事務環境的方法被調用完成以後session關閉。
5.經過以上的分析和總結能夠獲得若是直接在Action中調用Service層中的方法,若是出現異常,確定不能回滾,例以下面中的測試代碼:
很明顯,這裏有/0的異常,因此若是事務可以回滾,則不會發出insert的sql語句。可是結果倒是:
因此事務回滾失敗了。緣由就是Service對象中的每個方法執行完成以後都會當即關閉session,事務也隨之消失。相同Service中的方法調用屢次或者相同Service中的不一樣方法執行的時候必定不會在同一個事務中。
2、解決SSH整合以後的事務問題
解決方法就是使用OSIV模式(Open Session In View),即在MVC中的View層就開啓事務,這樣就擴展了事務的範圍。
只是在web.xml文件中加上以下的配置便可:
<!-- 使用OSIV模式解決事務回滾問題,只須要添加一個監聽器就能夠了 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
也就是說只是增長了一個過濾器而已,應當注意該過濾器放置的位置應當在下面的配置上面:
<filter> <filter-name>strutsFilter</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>strutsFilter</filter-name> <url-pattern>*.action</url-pattern> </filter-mapping>
不然添加的過濾器不生效。完整的web.xml文件配置以下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="3.0" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 7 8 9 <!-- 使用OSIV模式解決事務回滾問題,只須要添加一個監聽器就能夠了 --> 10 <filter> 11 <filter-name>OpenSessionInViewFilter</filter-name> 12 <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> 13 </filter> 14 <filter-mapping> 15 <filter-name>OpenSessionInViewFilter</filter-name> 16 <url-pattern>*.action</url-pattern> 17 </filter-mapping> 18 19 <filter> 20 <filter-name>strutsFilter</filter-name> 21 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 22 </filter> 23 <filter-mapping> 24 <filter-name>strutsFilter</filter-name> 25 <url-pattern>*.action</url-pattern> 26 </filter-mapping> 27 28 29 30 <listener> 31 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 32 </listener> 33 <context-param> 34 <param-name>contextConfigLocation</param-name> 35 <param-value>classpath:spring/applicationContext.xml</param-value> 36 </context-param> 37 38 <welcome-file-list> 39 <welcome-file>index.jsp</welcome-file> 40 </welcome-file-list> 41 </web-app>
最後,使用OSIV模式應當注意的事項:
1.使用OSIV模式以後,sessio的打開被提早了,session的關閉被延後了,這樣就解決了懶加載引發的異常的問題。
2.兩個過濾器,OSIV的過濾器必須在struts2以前。
使用OSIV模式的缺點:
由於Session的關閉被延遲了,因此hibernate的一級緩存在session中,因此會致使大量的緩存數據長期保存在了內存中。
3、Struts2流程回顧
1.啓動服務器的時候幹了兩件事情。
* dispatcher = init.initDispatcher(config);該句代碼加載了各類各樣的配置文件。
* init.initStaticContentLoader(config, dispatcher); 該句代碼完成了靜態注入。
2.當過濾器攔截到一個請求的時候作了什麼事情
查看doFilter方法中的源代碼。
(1)建立AciontContext對象。
建立ActionContext對象的同時會同時建立ValueStack對象,並且建立ValueStack對象在前,建立ActionContext對象在後。
兩個對象同時建立,並且兩個對象維護同一個Map對象,緣由是建立ActionContext的時候使用的參數是ValueStack對象的context成員變量。
經過代碼ActionContext.setContext(tx);便可以將ActionContext放置到ThreadLocal中了,這樣數據就安全了,下面是代碼追蹤。
(2)建立ActionProxy對象
init方法中執行了建立了對應的Action和全部的攔截器。
最後在invocation的invoke方法中執行全部的攔截器、執行當前請求的action、執行結果集。
4、Struts2的完整流程圖
最後附上項目源代碼:https://github.com/kdyzm/day53_ssh_oa