SSS框架整合no session(延遲載入)問題分析及解決
問題描述:
在做BOS物流管理系統的時候,點選區域頁面顯示配送區域資訊,這個時候後臺顯示了no session的錯誤
org.apache.struts2.json.JSONException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.itheima.domain.Area.subareas, could not initialize proxy - no Session at org.apache.struts2.json.JSONWriter.bean(JSONWriter.java:246) at org.apache.struts2.json.JSONWriter.processCustom(JSONWriter.java:178) at org.apache.struts2.json.JSONWriter.process(JSONWriter.java:168) at org.apache.struts2.json.JSONWriter.value(JSONWriter.java:134) at org.apache.struts2.json.JSONWriter.array(JSONWriter.java:492) at org.apache.struts2.json.JSONWriter.process(JSONWriter.java:158) at org.apache.struts2.json.JSONWriter.value(JSONWriter.java:134) at org.apache.struts2.json.JSONWriter.map(JSONWriter.java:447) at org.apache.struts2.json.JSONWriter.process(JSONWriter.java:154) at org.apache.struts2.json.JSONWriter.value(JSONWriter.java:134) at org.apache.struts2.json.JSONWriter.write(JSONWriter.java:102) at org.apache.struts2.json.JSONUtil.serialize(JSONUtil.java:107) at org.apache.struts2.json.JSONResult.createJSONString(JSONResult.java:203) at org.apache.struts2.json.JSONResult.execute(JSONResult.java:177) at com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:369) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:273) at org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:167) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265) at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:76) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:125) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:253) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171) at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:139) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189) at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54) at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:564) at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81) at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.itheima.domain.Area.subareas, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:563) at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:205) at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:542) at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:133) at org.hibernate.collection.internal.PersistentSet.equals(PersistentSet.java:423) at java.util.Vector.indexOf(Vector.java:411) at java.util.Vector.contains(Vector.java:370) at org.apache.struts2.json.JSONWriter.value(JSONWriter.java:117) at org.apache.struts2.json.JSONWriter.add(JSONWriter.java:401) at org.apache.struts2.json.JSONWriter.bean(JSONWriter.java:231) ... 81 more
看系統提示可以看出來是因為載入Area類中的subareas屬性出現了問題。我們再來看下Area類中有哪些屬性
@Id @Column(name = "C_ID") private String id; @Column(name = "C_PROVINCE") private String province; // 省 @Column(name = "C_CITY") private String city; // 城市 @Column(name = "C_DISTRICT") private String district; // 區域 @Column(name = "C_POSTCODE") private String postcode; // 郵編 @Column(name = "C_CITYCODE") private String citycode; // 城市編碼 @Column(name = "C_SHORTCODE") private String shortcode; // 簡碼 @OneToMany(mappedBy = "area") private Set<SubArea> subareas = new HashSet<SubArea>();
為什麼Area中這麼多屬性,只有subareas屬性出現了問題?可以看出來這裡的subareas是一個集合,問題就出現在這裡,在spring框架中集合屬性的查詢預設的是懶載入的,也就是說在service層呼叫dao層的查詢方法時,dao層並不會向資料庫傳送查詢語句,而是將查詢語句藏在了session的一級快取區域,在下一次subareas被使用到的時候才會真正向資料區傳送查詢語句,這樣的好處是可以為系統節省資源,但是同樣也帶來一個問題,因為我們第一次呼叫subareas是在web層將查詢到的資料打包成json資料的時候,但是我們的session在service層就已經被關閉了,我們都知道sss框架和ssh框架的底層都是Hibernate中的session物件對資料庫進行操作的。因此就會出現no session的異常。
問題解決:
第一種:讓session存活到web層
新增OpenEntityManagerInViewFilter過濾器(必須配置在struts攔截器前),原理就是將session的建立和關閉放在過濾器中,這樣就可以讓session存活到web層。
第二種:立即載入(不建議)
立即載入就是消除懶載入,運用JPA註解的屬性fetch=FetchType.EAGER
@OneToMany(mappedBy = "area",fetch=FetchType.EAGER)
private Set<SubArea> subareas = new HashSet<SubArea>();
這樣就可以在dao層立即載入subareas,這樣在web層就可以直接使用到subareas的資料了。
第三種:取消序列化
如果subareas這個屬性並不是需要的資料的話,可以通過註解讓json在序列化Area的屬性的時候,跳過subareas這個屬性。這樣就不需要向資料庫查詢subareas的資料了,也就不會出現懶載入的問題了。