Session is closed; nested exception is org.hibernate.SessionException: Session is closed解決方案
阿新 • • 發佈:2019-02-17
1.通過getSession()方法獲得session進行操作
Java程式碼
利用這種方式獲得的session在方法執行結束之後不會自動關閉連線,也就是說我們必須通過session.close()或者releaseSession(session)來手動進行關閉,否則會造成記憶體洩露或者連線耗盡等問題。手動關閉:
Java程式碼
如果對上述方法進行事務控制,那麼spring框架會自動為我們關閉session,此種情況(session.close())下再執行上述程式碼,會丟擲如下異常: Java程式碼
提示session已經關閉。但是如果在程式碼中通過releaseSession(session)的方法來關閉session,則不會丟擲異常。releaseSession(session)方法的程式碼如下:
Java程式碼
也就是說它是通過SessionFactoryUtils的releaseSession方法來實現的:
Java程式碼
可見它內部會先進行判斷。
檢視getSession()方法的原始碼:
Java程式碼
getSession()方法內部通過它的一個過載方法getSession(boolean allowCreate )來實現,變數allowCreate是HibernateTemplate中的變數,預設值為true,也就是建立一個新的session。如果我們呼叫getSession(false)來獲得session,那麼必須對其進行事務控制,原因是:(spring文件) Java程式碼
也就是說,getSession()方法從當前事務或者一個新的事務中獲得session,如果想從一個新的事務中獲得session(也就意味著當其不存在事務控制),則必須使HibernateTemplate中的allowCreate變數的值為”true”,而現在設定allowCreate變數的值為”false”就意味著無法從新的事務中獲得session,也就是隻能從當前事務中獲取,所以必須對當前方法進行事務控制,否則會丟擲如下異常:
Java程式碼
同時,如果對getSession()所在的方法進行事務控制,那麼類似如下的程式碼:
Java程式碼
只會開啟一個session,因為事務控制必須確保是同一個連線,spring會確保在整個相關方法中只存在一個session。Spring在方法開始時會開啟一個session(即使進行事務控制的方法內部不執行資料庫操作),之後在請求session時,如果在事務中存在一個未commit的session就返回,以此確保同一個session。
2.getCurrentSession()與openSession()
getCurrentSession()與openSession()方法通過Hibernate的SessionFactory獲得,兩者的區別網上有很多文章已經介紹過,即: Java程式碼
對於getCurrentSession()方法:
(1)其所在方法必須進行事務控制
(2)Session在第一次被使用的時候,或者第一次呼叫getCurrentSession()的時候,其生命週期就開始。然後它被Hibernate繫結到當前執行緒。當事務結束的時候,不管是提交還是回滾,Hibernate也會把Session從當前執行緒剝離,並且關閉它。假若你再次呼叫getCurrentSession(),你會得到一個新的Session,並且開始一個新的工作單元。
對於openSession()方法:
這個方法一般在spring與Hibernate的整合中不直接使用,它就是開啟一個session,並且這個session與上下文無關,如果對其所在方法進行事務控制,會發現不起作用,原因就是前面提到的,事務控制必須確保是同一個連線,而openSession()開啟的session與上下文無關。這個方法與getSession(),getCurrentSession()以及getHibernateTemplate()等方法的區別在於:後面的幾個方法spring可以對其進行控制,如果對它們所在的方法進行事務控制,spring可以確保是同一個連線,而openSession()方法,spring無法對其進行控制,所以事務也不會起作用。
3.OpenSessionInView
OpenSessionInView的主要功能是用來把一個Hibernate Session和一次完整的請求過程對應的執行緒相繫結。Open Session In View在request把session繫結到當前thread期間一直保持hibernate session在open狀態,使session在request的整個期間都可以使用,如在View層裡PO也可以lazy loading資料,如 ${ company.employees }。當View 層邏輯完成後,才會通過Filter的doFilter方法或Interceptor的postHandle方法自動關閉session。
Java程式碼
在業務方法中載入Group物件並將其儲存到HttpSession物件中
Java程式碼
注意Group採用預設的延遲載入機制,即此時返回的只是一個Group代理物件,
在jsp頁面中顯示group物件的users屬性,如下:
Java程式碼
此時會丟擲如下異常:
Java程式碼
延遲載入機制使得在業務方法執行結束之後僅僅返回Group的一個代理物件,在jsp頁面中使用到group物件的值時,才發出sql語句載入,但此時session已經關閉。解決方法是採用OpenSessionInView機制,在web.xml頁面中配置如下過濾器:
Java程式碼
總結:
(1) 對於getSession(),getSession(false),getCurrentSession()以及getHibernateTemplate()方法而言,如果對其所在方法進行事務控制,那麼可以確保在整個方法中只存在一個session,無論你執行了幾次CRUD操作,並且所開啟的session會在事務結束時自動關閉。
(2) 必須對getSession(false)以及getCurrentSession()所在的方法進行事務控制(原因見上述分析)
(3) 如果沒有對getSession()以及getHibernateTemplate()所在方法進行事務控制,那麼如果在方法中進行N次CRUD操作,就會開啟N個session,即每次呼叫getSession()和getHibernateTemplate()方法都會開啟新的session。這兩個方法的區別在於:getHibernateTemplate()方法結束時會自動關閉連線,而getSession()方法必須手動關閉。
(4) 如果在方法中採用SessionFactory的openSession()方法獲得連線進行操作,那麼無法對其進行事務控制。
(5) 一般的開發中,通常採用getHibernateTemplate()方法進行資料庫操作, getHibernateTemplate()方法採用模板+回撥的機制,進行資料庫操作很方便,可以檢視(其中session的開啟與關閉都是在doExecute方法中進行的)
Java程式碼
- public class Test extends HibernateDaoSupport{
- public void save(User user){
- this.getSession().save(user);
- }
- }
public class Test extends HibernateDaoSupport{ public void save(User user){ this.getSession().save(user); } }
利用這種方式獲得的session在方法執行結束之後不會自動關閉連線,也就是說我們必須通過session.close()或者releaseSession(session)來手動進行關閉,否則會造成記憶體洩露或者連線耗盡等問題。手動關閉:
Java程式碼
- public class Test extends HibernateDaoSupport{
- public void save(User user){
- Session session = this.getSession();
- session.save(user);
- session.close();
- // releaseSession(session); //這種方法,我用過的,絕對能解決session.close()的錯誤。
- }
- }
public class Test extends HibernateDaoSupport{ public void save(User user){ Session session = this.getSession(); session.save(user); session.close(); // releaseSession(session); } }
如果對上述方法進行事務控制,那麼spring框架會自動為我們關閉session,此種情況(session.close())下再執行上述程式碼,會丟擲如下異常: Java程式碼
- org.springframework.orm.hibernate3.HibernateSystemException: Session is closed; nested exception is org.hibernate.SessionException: Session is closed
- …
- org.hibernate.SessionException: Session is closed
org.springframework.orm.hibernate3.HibernateSystemException: Session is closed; nested exception is org.hibernate.SessionException: Session is closed
…
org.hibernate.SessionException: Session is closed
提示session已經關閉。但是如果在程式碼中通過releaseSession(session)的方法來關閉session,則不會丟擲異常。releaseSession(session)方法的程式碼如下:
Java程式碼
- protected final void releaseSession(Session session) {
- SessionFactoryUtils.releaseSession(session, getSessionFactory());
- }
protected final void releaseSession(Session session) {
SessionFactoryUtils.releaseSession(session, getSessionFactory());
}
也就是說它是通過SessionFactoryUtils的releaseSession方法來實現的:
Java程式碼
- public static void releaseSession(
- Session session,SessionFactory sessionFactory) {
- if (session == null) {
- return;
- }
- // Only close non-transactional Sessions.
- if (!isSessionTransactional(session,sessionFactory)) {
- closeSessionOrRegisterDeferredClose (session, sessionFactory);
- }
- }
public static void releaseSession(
Session session,SessionFactory sessionFactory) {
if (session == null) {
return;
}
// Only close non-transactional Sessions.
if (!isSessionTransactional(session,sessionFactory)) {
closeSessionOrRegisterDeferredClose (session, sessionFactory);
}
}
可見它內部會先進行判斷。
檢視getSession()方法的原始碼:
Java程式碼
- protected final Session getSession()
- throws DataAccessResourceFailureException, IllegalStateException {
- return getSession(this.hibernateTemplate.isAllowCreate());
- }
protected final Session getSession()
throws DataAccessResourceFailureException, IllegalStateException {
return getSession(this.hibernateTemplate.isAllowCreate());
}
getSession()方法內部通過它的一個過載方法getSession(boolean allowCreate )來實現,變數allowCreate是HibernateTemplate中的變數,預設值為true,也就是建立一個新的session。如果我們呼叫getSession(false)來獲得session,那麼必須對其進行事務控制,原因是:(spring文件) Java程式碼
- protected final org.hibernate.Session getSession()
- throws DataAccessResourceFailureException, IllegalStateException
- Get a Hibernate Session, either from the current transaction or a new one. The latter is only allowed if the "allowCreate" setting of this bean's HibernateTemplate is true.
protected final org.hibernate.Session getSession()
throws DataAccessResourceFailureException, IllegalStateException
Get a Hibernate Session, either from the current transaction or a new one. The latter is only allowed if the "allowCreate" setting of this bean's HibernateTemplate is true.
也就是說,getSession()方法從當前事務或者一個新的事務中獲得session,如果想從一個新的事務中獲得session(也就意味著當其不存在事務控制),則必須使HibernateTemplate中的allowCreate變數的值為”true”,而現在設定allowCreate變數的值為”false”就意味著無法從新的事務中獲得session,也就是隻能從當前事務中獲取,所以必須對當前方法進行事務控制,否則會丟擲如下異常:
Java程式碼
- java.lang.IllegalStateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here ...
java.lang.IllegalStateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here ...
同時,如果對getSession()所在的方法進行事務控制,那麼類似如下的程式碼:
Java程式碼
- Session session = null;
- for(int m =0;m<5;m++){
- Admin admin = new Admin();
- admin.setName("test");
- admin.setPassword("098");
- session = this.getSession();
- session.save(admin);
- }
Session session = null;
for(int m =0;m<5;m++){
Admin admin = new Admin();
admin.setName("test");
admin.setPassword("098");
session = this.getSession();
session.save(admin);
}
只會開啟一個session,因為事務控制必須確保是同一個連線,spring會確保在整個相關方法中只存在一個session。Spring在方法開始時會開啟一個session(即使進行事務控制的方法內部不執行資料庫操作),之後在請求session時,如果在事務中存在一個未commit的session就返回,以此確保同一個session。
2.getCurrentSession()與openSession()
getCurrentSession()與openSession()方法通過Hibernate的SessionFactory獲得,兩者的區別網上有很多文章已經介紹過,即: Java程式碼
- ①getCurrentSession建立的session會和繫結到當前執行緒,而openSession不會。
- ②getCurrentSession建立的執行緒會在事務回滾或事物提交後自動關閉,而openSession必須手動關閉
①getCurrentSession建立的session會和繫結到當前執行緒,而openSession不會。
②getCurrentSession建立的執行緒會在事務回滾或事物提交後自動關閉,而openSession必須手動關閉
對於getCurrentSession()方法:
(1)其所在方法必須進行事務控制
(2)Session在第一次被使用的時候,或者第一次呼叫getCurrentSession()的時候,其生命週期就開始。然後它被Hibernate繫結到當前執行緒。當事務結束的時候,不管是提交還是回滾,Hibernate也會把Session從當前執行緒剝離,並且關閉它。假若你再次呼叫getCurrentSession(),你會得到一個新的Session,並且開始一個新的工作單元。
對於openSession()方法:
這個方法一般在spring與Hibernate的整合中不直接使用,它就是開啟一個session,並且這個session與上下文無關,如果對其所在方法進行事務控制,會發現不起作用,原因就是前面提到的,事務控制必須確保是同一個連線,而openSession()開啟的session與上下文無關。這個方法與getSession(),getCurrentSession()以及getHibernateTemplate()等方法的區別在於:後面的幾個方法spring可以對其進行控制,如果對它們所在的方法進行事務控制,spring可以確保是同一個連線,而openSession()方法,spring無法對其進行控制,所以事務也不會起作用。
3.OpenSessionInView
OpenSessionInView的主要功能是用來把一個Hibernate Session和一次完整的請求過程對應的執行緒相繫結。Open Session In View在request把session繫結到當前thread期間一直保持hibernate session在open狀態,使session在request的整個期間都可以使用,如在View層裡PO也可以lazy loading資料,如 ${ company.employees }。當View 層邏輯完成後,才會通過Filter的doFilter方法或Interceptor的postHandle方法自動關閉session。
Java程式碼
- public class Group implements Serializable{
- private int id;
- private String name;
- private Set users;
- ...
- }
public class Group implements Serializable{
private int id;
private String name;
private Set users;
...
}
在業務方法中載入Group物件並將其儲存到HttpSession物件中
Java程式碼
- List groups = ht.find("from Group");
- Group group = (Group)groups.get(0);
- HttpSession session = ServletActionContext.getRequest().getSession();
- session.setAttribute("group", group);
List groups = ht.find("from Group");
Group group = (Group)groups.get(0);
HttpSession session = ServletActionContext.getRequest().getSession();
session.setAttribute("group", group);
注意Group採用預設的延遲載入機制,即此時返回的只是一個Group代理物件,
在jsp頁面中顯示group物件的users屬性,如下:
Java程式碼
- <%
- Group group = (Group)session.getAttribute("group");
- out.println(group.getUsers());
- %>
<%
Group group = (Group)session.getAttribute("group");
out.println(group.getUsers());
%>
此時會丟擲如下異常:
Java程式碼
- org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: entity.Group.users, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: entity.Group.users, no session or session was closed
延遲載入機制使得在業務方法執行結束之後僅僅返回Group的一個代理物件,在jsp頁面中使用到group物件的值時,才發出sql語句載入,但此時session已經關閉。解決方法是採用OpenSessionInView機制,在web.xml頁面中配置如下過濾器:
Java程式碼
- <filter>
- <filter-name>hibernateFilter</filter-name>
- <filter-class>
- org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
- </filter-class>
- </filter>
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
</filter>
總結:
(1) 對於getSession(),getSession(false),getCurrentSession()以及getHibernateTemplate()方法而言,如果對其所在方法進行事務控制,那麼可以確保在整個方法中只存在一個session,無論你執行了幾次CRUD操作,並且所開啟的session會在事務結束時自動關閉。
(2) 必須對getSession(false)以及getCurrentSession()所在的方法進行事務控制(原因見上述分析)
(3) 如果沒有對getSession()以及getHibernateTemplate()所在方法進行事務控制,那麼如果在方法中進行N次CRUD操作,就會開啟N個session,即每次呼叫getSession()和getHibernateTemplate()方法都會開啟新的session。這兩個方法的區別在於:getHibernateTemplate()方法結束時會自動關閉連線,而getSession()方法必須手動關閉。
(4) 如果在方法中採用SessionFactory的openSession()方法獲得連線進行操作,那麼無法對其進行事務控制。
(5) 一般的開發中,通常採用getHibernateTemplate()方法進行資料庫操作, getHibernateTemplate()方法採用模板+回撥的機制,進行資料庫操作很方便,可以檢視(其中session的開啟與關閉都是在doExecute方法中進行的)