首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

EJB 中整合 Hibernate 的關鍵技巧與常見陷阱

     編輯:Java 相關知識

場景代碼

public class HibernateTestBean implements SessionBean {
    SessionContext sessionContext;
    SessionFactory sf;

    public void setSessionContext(SessionContext sessionContext) {
        this.sessionContext = sessionContext;
        try {
            InitialContext ctx = new InitialContext();
            sf = (SessionFactory) ctx.lookup("java:/hibernate/SessionFactory");
        } catch (NamingException ex) { ex.printStackTrace(); }
    }

    public void tran() { tran1(); tran2(); }

    public void tran1() {
        Session session = sf.getCurrentSession();
        Message msg = new Message();
        msg.setCreateTime(new Date());
        msg.setDetail("trans1");
        session.save(msg);
        System.out.println("Session:" + session.hashCode());
        session.flush();
        session.close(); // 錯誤!
    }

    public void tran2() {
        Session session = sf.getCurrentSession(); // 將拋出異常
        Message msg = new Message();
        msg.setCreateTime(new Date());
        msg.setDetail("trans2");
        session.save(msg);
        System.out.println("Session:" + session.hashCode());
        // throw new RuntimeException("wrong");
    }
}

假設:EJB 採用 CMT,所有方法事務屬性為 Required。

問題分析

1. 為什麼 tran2 會拋出 "session is closed"?

  • sf.getCurrentSession() 返回當前事務上下文綁定的 Session (即 ThreadLocal 模式)
  • 事務邊界由容器管理 (CMT),Session 生命週期與事務同步:事務開始時開啟,事務提交/回滾時自動關閉
  • tran1 中手動呼叫 session.close() 導致 Session 提前關閉,但事務尚未結束
  • tran2 同屬同一事務 (Required 傳播),呼叫 getCurrentSession() 時容器嘗試返回同一 Session,發現已關閉 → 拋出異常

2. 正確做法:不要手動關閉 getCurrentSession()

原則:使用 getCurrentSession() 時,Session 由容器自動管理,開發者不可呼叫 close()、flush() (CMT 下 flush 也通常不需手動)。事務完成時容器自動 flush 並 close。

3. 若 tran1 改用 sf.openSession() 呢?

  • openSession() 每次創建新 Session,不綁定事務上下文
  • tran1 使用新 Session,手動 close 無妨
  • tran2 仍用 getCurrentSession() 取得容器管理的 Session,正常運作
  • 但兩個 Session 分屬不同事務上下文,tran1 的操作不受 tran2 事務控制,若 tran2 拋出 RuntimeException,tran1 已提交的資料無法回滾 (除非 tran1 也加入同一事務)

最佳實踐總結

場景建議
CMT 環境,需容器管理事務全程使用 sf.getCurrentSession(),絕不手動 close/flush
需手動控制 Session 生命週期 (BMT 或特殊需求)使用 sf.openSession(),自行管理 begin/commit/rollback/close
長會話 (Long Conversation)考慮 Hibernate 手動斷開/重新連接機制或 Spring OpenSessionInView

關鍵口訣

getCurrentSession() = 事務綁定 Session,容器負責開關;
openSession() = 手動 Session,自己負責開關。
CMT 下混用會導致事務邊界混亂,資料一致性風險極高。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved