初學者極易混淆 javax.servlet.http.HttpSession 與 org.hibernate.Session,兩者同名卻職責截然不同。本文從來源、生命周期、用途、作用域、代碼模式五個維度徹底釐清,並給出正確使用模板。
| 維度 | HttpSession (Web 會話) | Hibernate Session (持久化會話) |
|---|---|---|
| 全限定名 | javax.servlet.http.HttpSession | org.hibernate.Session |
| 核心職責 | 標識用戶身份,跨請求保存會話狀態 | 管理實體對象生命周期,執行 CRUD,事務邊界 |
| 創建者 | Servlet 容器(Tomcat/Jetty)自動創建 | 應用代碼手動獲取 SessionFactory.openSession() |
| 生命周期 | 用戶無操作超時(默認 30min)自動銷毀 | 業務邏輯開始→提交/回滾→手動關閉 |
| 作用域 | 單用戶、跨多個請求(同一瀏覽器會話) | 單線程、單業務事務(通常一個 Service 方法) |
| 存儲內容 | 用戶 ID、權限、購物車、CSRF Token 等 | 持久化實體、一級緩存、SQL 翻譯上下文 |
| 並發模型 | 多線程共享(需同步或用 ConcurrentHashMap) | 非線程安全,嚴禁多線程共享 |
| 獲取方式 | request.getSession() | sessionFactory.openSession() / getCurrentSession() |
| 典型用法 | session.setAttribute("user", user) | session.save(entity); session.createQuery(...) |
// 獲取或創建(首次訪問)
HttpSession session = request.getSession(); // 等同 request.getSession(true)
// 僅獲取不創建
HttpSession session = request.getSession(false); // 可能返回 null
// 存值
session.setAttribute("userId", 1001);
session.setAttribute("permissions", permList);
// 取值
Integer userId = (Integer) session.getAttribute("userId");
// 刪除單個
session.removeAttribute("tempData");
// 銷毀整個會話(登出)
session.invalidate();
// 設置超時(秒),-1 為永不超時
session.setMaxInactiveInterval(1800); // 30 分鐘
Map,Key 為 JSESSIONIDJSESSIONID,每次請求攜帶Session 共享(Redis、Sticky Session、DB 持久化)Hibernate Session = 工作單元 = 一級緩存 = 事務邊界 的載體
@Service
@Transactional // Spring 管理事務,自動綁定 Session 到線程
public class AccountService {
@Autowired SessionFactory sessionFactory;
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// 獲取當前事務綁定的 Session(Spring 自動管理 open/close)
Session session = sessionFactory.getCurrentSession();
Account from = session.get(Account.class, fromId);
Account to = session.get(Account.class, toId);
from.debit(amount);
to.credit(amount);
// 無需顯式 save/update,實體在 Session 管理下自動髒檢查
// @Transactional 提交時自動 flush → commit
}
}
Session session = sessionFactory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Account from = session.get(Account.class, 1L);
Account to = session.get(Account.class, 2L);
from.debit(amount); to.credit(amount);
tx.commit(); // flush + commit
} catch (Exception e) {
if (tx != null) tx.rollback();
throw e;
} finally {
session.close(); // 必須關閉,釋放 JDBC 連接、清理一級緩存
}
get(id) 返回同一對象引用| 場景 | 錯誤認知 | 正確理解 |
|---|---|---|
| 「把 Hibernate Session 放進 HttpSession」 | 跨請求復用數據庫連接 | ❌ 嚴禁!Session 非線程安全、連接會泄漏、實體變脫管狀態 |
| 「HttpSession 存實體對象」 | 方便下次請求直接用 | ⚠️ 可存 ID/DTO,不可存持久化實體(序列化失脫管、佔內存、並發衝突) |
| 「事務跨多個 HTTP 請求」 | 用戶向導式操作 | ❌ 反模式。用樂觀鎖/狀態機/臨時表實現長事務業務 |
| 「Session 就是數據庫連接」 | 一一對應 | Session 內部持有 Connection,但有連接池複用,Session 關閉不一定關閉物理連接 |
// 配置類
@Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
LocalSessionFactoryBean sf = new LocalSessionFactoryBean();
sf.setDataSource(dataSource);
sf.setPackagesToScan("com.example.entity");
sf.setHibernateProperties(hibernateProps());
return sf;
}
@Bean
public HibernateTransactionManager txManager(SessionFactory sf) {
return new HibernateTransactionManager(sf);
}
// Service 只需 @Transactional
@Service
@Transactional
public class OrderService { ... }
@Transactional 自動綁定 Session 到線程,業務代碼無感知