1. 程式人生 > >hibernate中session的獲取使用以及其他注意事項

hibernate中session的獲取使用以及其他注意事項

hibernate中session的獲取使用以及其他注意事項

前言:工作時,在同時使用Hibernate的getSession()、getHibernateTemplate()獲取Session後進行資料查詢時不是出現了"session is close"異常就是出現其他異常問題,痛定思痛,決定收集並整理相關資料,方便今後的使用。

一、session的獲取

在hibernate中的Session物件通過SessionFactory來管理,可以通過使用openSession ()、getSession()和getCurrentSession()方法來獲取session。

Spring和Hibernate的整合的一個要點就是對事務的支援,openSession、getCurrentSession都是程式設計式事務(手動設定事務的提交、回滾)中重要的物件,HibernateDaoSupport則提供了更方便的宣告式事務支援。(引自:

lmdcszh的 由openSession、getCurrentSession和HibernateDaoSupport淺談Spring對事物的支援

  注:在 SessionFactory 啟動的時候, Hibernate 會根據配置建立相應的 CurrentSessionContext ,在 getCurrentSession() 被呼叫的時候,實際被執行的方法是 CurrentSessionContext.currentSession() 。在 currentSession() 執行時,如果當前 Session 為空, currentSession 會呼叫 SessionFactory 的 openSession 。

  所以 getCurrentSession() 對於 Java EE 來說是更好的獲取 Session 的方法。

  二、openSession ()、getSession()和getCurrentSession()方法的區別

  以下內容引自jpbirdy的文章“Hibernate中的openSession(),getSession() 和 getCurrentSession() 的區別”。講的非常好,我就不一一細述,文章內容如下:


開發中,使用MyEclipse自動生成的Hibernate DAO類中在對session的獲取中,都使用的getSession(MyEclipse8.5之前的版本都是如此)

首先要說明一下這3個方法:

1、openSession 和 getCurrentSession這兩個方法是 Hibernate中的sessionFactory中的方法。要獲取session只能通過這兩個方法獲取。

2、getSession這個方法是使用MyEclipse的Hibernate工具時自動生成的方法。進入方法裡檢視: 

複製程式碼
public static Session getSession() throws HibernateException {
  Session session = (Session) threadLocal.get();//注1
  if (session == null || !session.isOpen()) {
    if (sessionFactory == null) {
      rebuildSessionFactory();//注2
    }
    session = (sessionFactory != null) ? sessionFactory.openSession(): null;
    threadLocal.set(session);
  }
  return session;
}
複製程式碼

  很明顯,Myeclipse使用的是openSession。所以上述的3個方法,其實也就是openSession和getCurrentSession的區別。

  這裡簡單說明一下openSession 和 getCurrentSession這兩個方法的區別。

  很容易看出來,openSession每次都是建立一個新的session,而getCurrentSession會檢視,如果當前上下文中已經有Session,則直接使用原有的,否則會建立一個新的session。

  當然這樣說並不是很形象。很多人會理解為,後面一個只是前面的略微優化而已,每次都開啟新的也不會出錯。當然這樣理解並沒有完全錯誤,其實這裡的Session使用上沒有很大的區別,區別是在於使用openSession時,如果涉及到對資料庫的資料的修改(增、刪、改),在事務提交之後,session必須手動關閉(session.close()),否則會出錯,一般是資料已經儲存進資料庫,但使用Hibernate再次查詢資料時並沒有顯示新的值。而getCurrentSession在事務提交後會自動關閉當前session。所以,一般推薦使用getCurrentSession,這樣需要我們關心的內容就更少,而且更不容易出錯。   當然,MyEclipse也不會放著高階的東西不用的,仔細檢視上面的程式碼,發現在openSession之前,session會先從threadLocal中獲取,這裡的threadLocal就可以類比為getCurrentSession中配置的thread,所以,可以認為MyEclipse是在openSession之前已經實現了從執行緒中獲取上下文的Session,所以直接使用MyEclipse生成的getSession的功能類似getCurrentSession,但是測試後發現,getSession在提交事務之後,並沒有自動關閉,仍然需要手動關閉。   

  為什麼MyEclipse不使用getCurrentSession呢?
  檢視生成的DAO類可以發現,MyEclipse生成的是沒有和事務相關的操作的,當然在進行save、update、delete操作時必須手動新增事務開始和事務提交,所以這裡在提交事務之後必須手動關閉session。而進行資料庫查詢操作時,openSession是不需要提交事務的。getCurrentSession則必須要開啟一個新的事務,否則會報createQuery is not valid without active transaction錯誤。

  從簡化操作的角度,直接使用MyEclipse的方法更為簡單,但要記住增刪改操作必須手動新增事務,並在操作結束後關閉session。


註釋:

  注1-ThreadLocal

  其實ThreadLocal並非是一個執行緒的本地實現版本,它並不是一個Thread,而是threadlocalvariable(執行緒區域性變數)。

  執行緒區域性變數(ThreadLocal)其實的功用非常簡單,就是為每一個使用該變數的執行緒都提供一個變數值的副本,是Java中一種較為特殊的執行緒繫結機制,是每一個執行緒都可以獨立地改變自己的副本,而不會和其它執行緒的副本衝突。

  常用API:

  T get( )      --返回此執行緒區域性變數的當前執行緒副本中的值,如果這是執行緒第一次呼叫該方法,則建立並初始化此副本。

  void set(T value)  --將此執行緒區域性變數的當前執行緒副本中的值設定為指定值。許多應用程式不需要這項功能,它們只依賴於 initialValue() 方法來設定執行緒區域性變數的值。

  (引自:深入研究java.lang.ThreadLocal類

  注2-rebuildSessionFactory 重新載入hibernate,在HibernateUtil中的具體實現如下:

public static void rebuildSessionFactory() {
    log.debug("Using current Configuration to rebuild SessionFactory");
    rebuildSessionFactory(configuration);
}
複製程式碼
public static void rebuildSessionFactory(Configuration cfg) {
    log.debug("Rebuilding the SessionFactory from given Configuration");
    if (sessionFactory != null && !sessionFactory.isClosed())
        sessionFactory.close();
    if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null) {
      log.debug("Managing SessionFactory in JNDI");
      cfg.buildSessionFactory();
    } else {
        log.debug("Holding SessionFactory in static variable");
        sessionFactory = cfg.buildSessionFactory();
    }
    configuration = cfg;
}        
複製程式碼

 HibernateUtil 原始碼:請點選!

總結:

  1、openSession和getCurrentSession的根本區別在於有沒有綁定當前執行緒,所以,使用方法有差異:

* openSession 沒有綁定當前執行緒,所以,使用完後必須關閉。重新建立一個新的session。

* currentSession 和當前執行緒繫結,在事務結束後會自動關閉。使用當前的session。

  2、在一個應用程式中,如果DAO 層使用Spring 的hibernate 模板,通過Spring 來控制session 的生命週期,則首選getCurrentSession ()。

  以下內容引自lmdcszh由openSession、getCurrentSession和HibernateDaoSupport淺談Spring對事物的支援,想看原文的可直接點選此連結。

 三、事務的邊界和傳播

  通常情況下事務的邊界需要設定在業務邏輯處理層中,但是若在一個業務中涉及到多個業務邏輯層之間的方法,且這些方法需要在同一個事務中執行,那這就涉及到了事務的傳播性。

如果使用openSession,就要在dao層的方法中傳遞session,而這種做法是很糟糕的,首先增加了引數的個數,另外,方法是否需要事務,完全是可以當做一種獨立的服務抽離出的。

因為currentSession是執行緒級別的,所以,只要業務邏輯方法在同一個執行緒中,就不會擔心上面的問題。這也是currentSession的一個優越處之一。

  使用currentSession:

  1.在配置檔案中將執行緒配置成Thread級別的。

<property name="hibernate.current_session_context_class">thread</property>

  2.呼叫sessionFactory的getCurrentSession方法:

複製程式碼
 1 publicvoid addUser(User user) {
 2   Session session = null;
 3   try {
 4     session =this.getSessionFactory().getCurrentSession();
 5     session.beginTransaction();
 6     session.save(user);
 7     //記錄操作日誌,此處略
 8     session.getTransaction().commit();
 9   }catch(Exception e) {
10     e.printStackTrace();
11     session.getTransaction().rollback();
12   }
13 }
複製程式碼

   使用openSession:

複製程式碼
 1 public void addUser(User user) {
 2   Session session = null;
 3   try{
 4     session= this.getSession();
 5     session.beginTransaction();
 6     // 若干操作…………
 7     session.getTransaction().commit();
 8   }catch(Exceptione) {
 9     e.printStackTrace();
10     session.getTransaction().rollback();
11   }finally{
12     HibernateUtils.closeSession(session);
13   }
複製程式碼

   使用HibernateDaoSupport宣告式事務:即使用this.getHibernateTemplate()方法時可呼叫的方法,如save、update等。

  Spring與Hibernate的整合使用最多的是HibernateDaoSupport,它對session的獲取以及事務做了進一步的封裝,只需要關注dao的實現,而不用擔心某個地方的事務是否關閉。

  四、關於異常與事務回滾

    Spring在遇到執行期異常(繼承了RuntimeException)的時候才會回滾,如果是Exception(如使用者輸入密碼錯誤)丟擲就好,事務會繼續往下進行。

    Spring對異常的處理的靈活性還是比較高的,可以配置遇到某個Exception進行回滾,某個RuntimeException不回滾,但是對於EJB就沒有這麼靈活了,EJB相當於是固定的套餐。

  不會回滾:  

1 public void addUser(User user)  throws Exception {  
2   this.getHibernateTemplate().save(user);  
3   //若干操作……            
4   throw new Exception();  
5 } 

   回滾:

1 public void addUser(User user) {  
2     this.getHibernateTemplate().save(user);      
3     //若干操作……         
4     throw new RuntimeException();  
5  } 

  五、事務傳播特性

  為了保證呼叫的業務邏輯方法都使用同一個事務,通常都使用REQUIRED這個級別,它表示:如果上一個方法中有事務,就直接使用,如果沒有,就建立一個事務,這樣,一旦事務建立了後,後續呼叫的方法就不會再建立。

  其他的事務傳播特性見下表:

  六、Spring事務的隔離級別

  1. ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager預設的隔離級別,使用資料庫預設的事務隔離級別。

        另外四個與JDBC的隔離級別相對應。

  2. ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的資料。

        這種隔離級別會產生髒讀,不可重複讀和幻像讀。

  3. ISOLATION_READ_COMMITTED: 保證一個事務修改的資料提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的資料

  4. ISOLATION_REPEATABLE_READ: 這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。

        它除了保證一個事務不能讀取另一個事務未提交的資料外,還保證了避免下面的情況產生(不可重複讀)。

  5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。

       除了防止髒讀,不可重複讀外,還避免了幻像讀。

      事務隔離級別主要應用在對大資料的處理方面,與鎖的機制是密不可分的,這裡不贅述。

 

 Ps 推薦 新Hibernate SessionFactory().getCurrentSession()貓膩

文章轉自:https://www.cnblogs.com/ylhssn/p/4270333.html