一、概述
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。執行。
兩次打印結果相同。
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} 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} View Code原因:數據庫的隔離級別,Mysql 默認隔離級別為 REPEATABLE READ。
在 Hibernate 的配置文件中可以顯式的設置隔離級別. 每一個隔離級別都對應一個整數:
1. READ UNCOMMITED
2. READ COMMITED
4. REPEATABLE READ
8. SERIALIZEABLE
Hibernate 通過為 Hibernate 映射文件指定 hibernate.connection.isolation 屬性來設置事務的隔離級別。
修改後的打印結果:
兩次打印結果不同。
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='jerry', date=2016-09-28} 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} View Code(3)clear():清理緩存。
@Test public void testClear() { session.get(News.class, 1); session.clear(); session.get(News.class, 1); }
輸出結果:
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=? 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=? View Code三、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。