1.通過getSession()方法獲得session進行操作
-
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在方法執(zhí)行結束之后不會自動關閉連接,也就是說我們必須通過session.close()或者releaseSession(session)來手動進行關閉,否則會造成內(nèi)存泄露或者連接耗盡等問題。手動關閉:
-
public
?
class
?Test??
extends
?HibernateDaoSupport{ ??
-
?????
public
?
void
?save(User?user){ ??
-
????????Session?session?=?
this
.getSession(); ??
-
????????session.save(user); ??
-
????????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,此種情況下再執(zhí)行上述代碼,會拋出如下異常:
-
?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已經(jīng)關閉。但是如果在代碼中通過releaseSession(session)的方法來關閉session,則不會拋出異常。releaseSession(session)方法的代碼如下:
-
protected
?
final
?
void
?releaseSession(Session?session)?{ ??
-
????SessionFactoryUtils.releaseSession(session,?getSessionFactory()); ??
-
}??
protected final void releaseSession(Session session) {
SessionFactoryUtils.releaseSession(session, getSessionFactory());
}
也就是說它是通過SessionFactoryUtils的releaseSession方法來實現(xiàn)的:
-
public
?
static
?
void
?releaseSession(? ??
-
?????Session?session,SessionFactory?sessionFactory)?{ ??
-
??????????
if
?(session?==?
null
)?{ ??
-
??????????????
return
; ??
-
??????????} ??
-
??????????
??
-
??????????
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);
}
}
可見它內(nèi)部會先進行判斷。
查看getSession()方法的源碼:
-
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()方法內(nèi)部通過它的一個重載方法getSession(boolean allowCreate )來實現(xiàn),變量allowCreate是HibernateTemplate中的變量,默認值為true,也就是創(chuàng)建一個新的session。如果我們調(diào)用getSession(false)來獲得session,那么必須對其進行事務控制,原因是:(spring文檔)
-
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”,而現(xiàn)在設置allowCreate變量的值為”false”就意味著無法從新的事務中獲得session,也就是只能從當前事務中獲取,所以必須對當前方法進行事務控制,否則會拋出如下異常:
-
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()所在的方法進行事務控制,那么類似如下的代碼:
-
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(即使進行事務控制的方法內(nèi)部不執(zhí)行數(shù)據(jù)庫操作),之后在請求session時,如果在事務中存在一個未commit的session就返回,以此確保同一個session。
2.getCurrentSession()與openSession()
getCurrentSession()與openSession()方法通過Hibernate的SessionFactory獲得,兩者的區(qū)別網(wǎng)上有很多文章已經(jīng)介紹過,即:
-
①getCurrentSession創(chuàng)建的session會和綁定到當前線程,而openSession不會。? ??
-
②getCurrentSession創(chuàng)建的線程會在事務回滾或事物提交后自動關閉,而openSession必須手動關閉??
①getCurrentSession創(chuàng)建的session會和綁定到當前線程,而openSession不會。
②getCurrentSession創(chuàng)建的線程會在事務回滾或事物提交后自動關閉,而openSession必須手動關閉
對于getCurrentSession()方法:
??????? (1)其所在方法必須進行事務控制
??????? (2)Session在第一次被使用的時候,或者第一次調(diào)用getCurrentSession()的時候,其生命周期就開始。然后它被Hibernate綁定到當前線程。當事務結束的時候,不管是提交還是回滾,Hibernate也會把Session從當前線程剝離,并且關閉它。假若你再次調(diào)用getCurrentSession(),你會得到一個新的Session,并且開始一個新的工作單元。????
??
對于openSession()方法:
???????? 這個方法一般在spring與Hibernate的集成中不直接使用,它就是打開一個session,并且這個session與上下文無關,如果對其所在方法進行事務控制,會發(fā)現(xiàn)不起作用,原因就是前面提到的,事務控制必須確保是同一個連接,而openSession()打開的session與上下文無關。這個方法與getSession(),getCurrentSession()以及getHibernateTemplate()等方法的區(qū)別在于:后面的幾個方法spring可以對其進行控制,如果對它們所在的方法進行事務控制,spring可以確保是同一個連接,而openSession()方法,spring無法對其進行控制,所以事務也不會起作用。
3.OpenSessionInView
OpenSessionInView的主要功能是用來把一個Hibernate Session和一次完整的請求過程對應的線程相綁定。Open Session In View在request把session綁定到當前thread期間一直保持hibernate session在open狀態(tài),使session在request的整個期間都可以使用,如在View層里PO也可以lazy loading數(shù)據(jù),如 ${ company.employees }。當View 層邏輯完成后,才會通過Filter的doFilter方法或Interceptor的postHandle方法自動關閉session。
-
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;
...
}
在業(yè)務方法中加載Group對象并將其保存到HttpSession對象中
-
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屬性,如下:
-
<%?? ??
-
?????Group?group?=?(Group)session.getAttribute(
"group"
); ??
-
?????out.println(group.getUsers()); ??
-
%>???
<%
Group group = (Group)session.getAttribute("group");
out.println(group.getUsers());
%>
此時會拋出如下異常:
-
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
延遲加載機制使得在業(yè)務方法執(zhí)行結束之后僅僅返回Group的一個代理對象,在jsp頁面中使用到group對象的值時,才發(fā)出sql語句加載,但此時session已經(jīng)關閉。解決方法是采用OpenSessionInView機制,在web.xml頁面中配置如下過濾器:
-
<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,無論你執(zhí)行了幾次CRUD操作,并且所打開的session會在事務結束時自動關閉。
(2) 必須對getSession(false)以及getCurrentSession()所在的方法進行事務控制(原因見上述分析)
(3) 如果沒有對getSession()以及getHibernateTemplate()所在方法進行事務控制,那么如果在方法中進行N次CRUD操作,就會打開N個session,即每次調(diào)用getSession()和getHibernateTemplate()方法都會打開新的session。這兩個方法的區(qū)別在于:getHibernateTemplate()方法結束時會自動關閉連接,而getSession()方法必須手動關閉。
(4) 如果在方法中采用SessionFactory的openSession()方法獲得連接進行操作,那么無法對其進行事務控制。
(5) 一般的開發(fā)中,通常采用getHibernateTemplate()方法進行數(shù)據(jù)庫操作, getHibernateTemplate()方法采用模板+回調(diào)的機制,進行數(shù)據(jù)庫操作很方便,可以查看(其中session的打開與關閉都是在doExecute方法中進行的):
http://lijiejava.javaeye.com/blog/667644
http://lijiejava.javaeye.com/blog/727249
轉自:
http://www.javaeye.com/topic/733971