一、概述
Session 是 Hibernate 向應用程序提供操縱數據的主要接口,它提供了基本的保存、更新、刪除和加載 Java 對象的方法。
二、Session 緩存
1.簡介
(1)Session 有一個緩存,稱為 Hibernate 一級緩存。位於緩存中的對象稱為持久化對象,每一個持久化對象與數據庫中的一條記錄對應。
(2)站在持久化的角度,Hibernate 將對象分為 4 種狀態:臨時狀態、持久化狀態、游離狀態、刪除狀態。
2.測試 Session 緩存
(1)准備
①hibernate.cfg.xml 文件請參看上一篇文章。
②SessionFactory、Session、Transaction
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
@Before
public void init() {
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
transaction = session.beginTransaction();
}
@After
public void destroy() {
transaction.commit();
session.close();
sessionFactory.close();
}
說明:使用單元測試類進行測試。因為是測試環境,不存在並發的情況,創建了一個 Session 對象。
(2)測試
@Test
public void testSession() {
News news = (News) session.get(News.class, 1);
System.out.println(news);
News news2 = (News) session.get(News.class, 1);
System.out.println(news2);
System.out.println(news.equals(news2));
}
測試結果:
Hibernate:
select
news0_.id as id1_0_0_,
news0_.title as title2_0_0_,
news0_.author as author3_0_0_,
news0_.date as date4_0_0_
from
hibernate.news news0_
where
news0_.id=?
News{id=1, title='Title', author='tom', date=2016-09-28}
News{id=1, title='Title', author='tom', date=2016-09-28}
true
說明:
第一次查詢的時候,會將引用賦值給 news,同時向 Session 緩存中存入了一份。
第二次查詢的時候,並沒有發送 select 語句,而是從 Session 緩存中直接獲取的。
3.操縱 Session 緩存

(1)flush() :使數據表中的記錄和 Session 緩存中的對象的狀態保持一致。
① 在 Transaction 的 commit() 方法中,先調用 session 的 flush 方法,再提交事務。
org.hibernate.engine.transaction.spi.AbstractTransactionImpl#commit
@Override
public void commit() throws HibernateException {
if ( localStatus != LocalStatus.ACTIVE ) {
throw new TransactionException( "Transaction not successfully started" );
}
LOG.debug( "committing" );
beforeTransactionCommit();
try {
doCommit();
localStatus = LocalStatus.COMMITTED;
afterTransactionCompletion( Status.STATUS_COMMITTED );
}
catch ( Exception e ) {
localStatus = LocalStatus.FAILED_COMMIT;
afterTransactionCompletion( Status.STATUS_UNKNOWN );
throw new TransactionException( "commit failed", e );
}
finally {
invalidate();
afterAfterCompletion();
}
}
org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction#beforeTransactionCommit
protected void beforeTransactionCommit() {
this.transactionCoordinator().sendBeforeTransactionCompletionNotifications(this);
if(this.isDriver && !this.transactionCoordinator().getTransactionContext().isFlushModeNever()) {
this.transactionCoordinator().getTransactionContext().managedFlush();
}
if(this.isDriver) {
this.transactionCoordinator().getTransactionContext().beforeTransactionCompletion(this);
}
}
② 可能會打印 SQL 語句,但是不會提交事務。
③ 在未提交事務或顯式的調用 flush() 方法前,也可能會進行 flush() 操作。
(2)refresh():會強制發送 SELECT 語句,以使 Session 緩存中對象的狀態和數據表中對應的記錄保持一致。
1 @Test
2 public void testRefresh() {
3 News news = (News) session.get(News.class, 1);
4 System.out.println(news);
5 session.refresh(news);
6 System.out.println(news);
7 }
我在第5行斷點,然後修改數據庫中 News 的 `author` 字段,改為 jerry。執行。
兩次打印結果相同。

原因:數據庫的隔離級別,Mysql 默認隔離級別為 REPEATABLE READ。
在 Hibernate 的配置文件中可以顯式的設置隔離級別. 每一個隔離級別都對應一個整數:
1. READ UNCOMMITED
2. READ COMMITED
4. REPEATABLE READ
8. SERIALIZEABLE
Hibernate 通過為 Hibernate 映射文件指定 hibernate.connection.isolation 屬性來設置事務的隔離級別。
修改後的打印結果:
兩次打印結果不同。

(3)clear():清理緩存。
@Test
public void testClear() {
session.get(News.class, 1);
session.clear();
session.get(News.class, 1);
}
輸出結果:

三、Session API
1.四種狀態的轉換圖

(1)臨時對象
(2)持久化對象
(3)游離對象
(4)刪除對象
2.save()
(1)將一個臨時對象轉變為持久化對象
(2)為對象分配 ID
(3)在 flush 緩存的時候,計劃執行一條 INSERT 語句
(4)在 save() 方法前的 id 是無效的
(5)持久化對象的 ID 是不能被更改的。因為 Hibernate 通過持久化對象的 OID 來維持它與數據庫相關記錄的對應關系。
* persist() 和 save() 區別
對一個 OID 不為 Null 的對象執行 save() 方法時,會把該對象以一個新的 OID 保存到數據庫中,而 persist() 則會拋出一個異常。
3.get()/load()
(1)都可以根據 OID 從數據庫中加載一個持久化對象。
(2)執行 get() 時會立即加載對象。執行 load() ,若不使用該對象,則不會立即執行查詢操作,而是返回一個代理對象。
(3)get() 是立即檢索,而 load() 是延遲檢索。
(4)若數據表中沒有對應記錄,Session 也沒有被關閉。get() 返回 null,load() 使用返回對象時拋出異常。
(5)load() 可能會拋出 LozyInitizationException 異常:在需要初始化代理對象之前已經關閉了 Session。
@Test
public void testLoad() {
News news = (News) session.load(News.class, 1);
session.close();
System.out.println(news);
}
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
4.update()
(1)將一個游離對象轉變為持久化對象,並且計劃執行一條 update 語句。
(2)若更新一個持久化對象,不需要顯式的調用 update() 方法。因為在調用 Transaction 的 commit() 方法時,會先執行 session 的 flush() 方法。
(3)注意
5.saveOrUpdate()
(1)同時包含了 save() 和 update() 方法的功能。
(2)判斷是否是游離對象還是臨時對象是根據 對象的 OID 來判定的。若為 null ,則執行 save() ,若不為 null,則判定為游離對象,執行 update() 。
(3)若 OID 不為 null,但數據中還沒有與之對應的記錄,則會拋出一個異常。
(4)了解:OID 值等於 id 的 unsaved-value 屬性值的對象,也被認為是一個游離對象。
6.delete()
(1)既可以刪除一個游離對象,也可以刪除一個持久化對象。
(2)只要 OID 和數據表中一條記錄對應,就會准備執行 delete 操作,若 OID 在數據表中沒有對應的記錄,則拋出異常。
(3)在執行 delete() 後,還是可以獲取到對象的 OID,防止對該對象的其他持久化操作,可以通過設置 hibernate 配置文件的 hibernate.use_identifier_rollback 為 true,
使刪除對象後,把其 OID 值為 null。
7.evict()
把指定持久化對象從 session 緩存中移除。
8.調用存儲過程
@Test
public void testWork() {
session.doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
System.out.println(connection);
// 調用存儲過程
}
});
}
四、總結
介紹了 Hibernate 的一級緩存,包括如何操縱 Session 的緩存,以及四種狀態之間的轉換,以及建立在 Session 緩存和四種狀態基礎上的 Session API。