session接口是Hibernate向應用程序提供的操縱數據庫的最主要接口,其提供有基本的保存、更新、刪除和加載java對象的方法。 Session具有一個緩存,位于緩存中的對象稱為持久化對象,其與數據表中相關記錄相對應;其中,Session對象能夠在某些時間點,按照緩存中對象的變化來執行對應SQL語句,以同步更新數據庫,該過程被稱為刷新緩存(flush)。 站在持久化的角度來看,Hibernate將對象分為四種狀態:持久化狀態、臨時狀態、游離狀態和刪除狀態,Session的特定方法能使對象從一個狀態轉換到另一個狀態。
1. Session緩存
概念:在Session接口的實現中包含一系列的Java集合,這些集合構成Session緩存。 **作用:**Session緩存可減少Hibernate應用程序訪問數據庫的頻率,因為只要Session實例沒有結束生命周期且沒有清理緩存,則存放在其緩存中的對象也不會消失。 其中,Session緩存的基本原理測試代碼如下所示:
// 注意:只會向數據庫發送一條SQL語句News news1 = (News) session.get(News.class, 1);System.out.PRintln(news1);News news2 = (News) session.get(News.class, 1);System.out.println(news2);System.out.println(news1 == news2); // true1.1 flush緩存
作用: Session按照緩存中對象的屬性變化來同步更新數據庫中的表記錄。 實現:刷新緩存的時間點為session.flush(); 或 transaction.commit()。 比較: Session對象的flush()方法可能會執行SQL語句,但不提交事務;而Transaction對象的commit()方法先調用flush()方法,再提交事務(將對數據庫的操作永久保存)。 設定刷新緩存的時間點:可以調用Session對象的setFlushMode()方法顯式設定刷新緩存的時間點,具體模式如下圖所示:
注意:在未提交事務或顯式調用Session對象flush()方法,也可能會進行緩存刷新操作,具體如下:
執行HQL或QBC查詢時:若緩存中持久化對象的屬性發生變化,則會先刷新緩存,以保證查詢結果能夠反映持久化對象的最新狀態;執行save()方法保存對象時:若對象使用native生成器生成OID,則執行保存操作時,會立即執行刷新操作以保證對象的ID是存在的。1.2 refresh緩存
作用:強制發送SELECT語句,以使Session緩存中對象的狀態和數據表中對應的記錄保持一致。 注意:MySQL數據庫的默認事務隔離級別為“REPEATABLE READ”,需要手動修改為“READ COMMITED”。
1.3 clear緩存
作用:Session對象的clear()方法可以清除Session緩存中的所有緩存對象。
2. 數據庫的隔離級別
數據庫的隔離性是指隔離并發運行各個事務的能力,而隔離級別是指一個事務與其他事務的隔離程度;隔離級別越高,數據一致性就越好,但并發性越弱。
2.1 并發問題概述
對于同時運行的多個事務,當這些事務訪問數據庫中相同數據時,如果沒有采取必要的隔離機制,會導致各種并發問題,如下:
臟讀:對于兩個事務T1和T2,T1讀取了已經被T2更新但還沒有被提交的字段后,若T2回滾,T1讀取的內容就是臨時且無效的;不可重復讀:對于兩個事務T1和T2,T1讀取一個字段后,T2更新了該字段;之后T1再次讀取同一個字段時值發生變化;幻讀:對于兩個事務T1和T2,T1從一個表中讀取了一個字段后,T2在該表中插入一些新的行;之后,如果T1再次讀取同一個表,就會多出幾行。2.2 事務隔離級別
數據庫提供的四種事務隔離級別,分別是:
其中,Oracle 支持“READ COMMITED”(默認)和“SERIALIZABLE”兩種事務隔離級別,而MySQL支持四種事務隔離級別,默認為“READ COMMITED”。
2.2 在MySQL中設置隔離級別
在MySQL數據庫中,每啟動一個程序就會獲得一個單獨的數據庫連接,每個數據庫連接都有一個全局變量@@tx_isolation,表示當前的事務隔離級別。
查看當前的隔離級別:SELECT @@tx_isolation;設置當前MySQL連接的隔離級別:set transaction isolation level read committed;設置當前MySQL數據庫的隔離級別:set global transaction isolation level read committed。2.3 在Hibernate中設置隔離級別
JDBC數據庫連接使用數據庫系統默認的隔離級別。在Hibernate中可通過配置其hibernate.connection.isolation屬性的方式來設置事務的隔離級別,具體說明如下:
<!-- 1). 配置數據庫的事務隔離級別為:READ UNCOMMITED --><property name="hibernate.connection.isolation">1</property><!-- 2). 配置數據庫的事務隔離級別為:READ COMMITED --><property name="hibernate.connection.isolation">2</property><!-- 3). 配置數據庫的事務隔離級別為:REPEATABLE READ --><property name="hibernate.connection.isolation">4</property><!-- 4). 配置數據庫的事務隔離級別為:SERIALIZEABLE --><property name="hibernate.connection.isolation">8</property>
3. 持久化對象的狀態
站在持久化的角度,Hibernate把對象分為四種狀態:持久化狀態、臨時狀態、游離狀態、刪除狀態;Session的特定方法能使對象從一個狀態轉換到另一個狀態。
3.1 臨時對象(Transient)
在使用代理主鍵的情況下,OID通常為null;不處于Session緩存中;在數據庫中沒有對應的記錄。3.2 持久化對象(Persist)
OID不為null;位于Session緩存中;若在數據庫中已經有與其對應的記錄,持久化對象和數據庫中的相關記錄對應;Session 在flush緩存時,會根據持久化對象的屬性變化,來同步更新數據庫;在同一個Session實例的緩存中, 數據庫表中每條記錄只對應唯一的持久化對象。3.3 刪除對象(Removed)
在數據庫中沒有和其OID對應的記錄;不再處于Session緩存中;一般情況下,應用程序不該再使用被刪除的對象。3.4 游離對象(Detached)
OID不為null;不再處于Session緩存中;一般情況下,游離對象是由持久化對象轉變而來,在數據庫中可能還存在與其對應的記錄。3.5 對象狀態轉換圖

4. Session核心方法
4.1 save()與persist()方法
/** * Session對象的save()方法: * 1). 將臨時對象加入Session緩存中,使其轉變為持久化對象; * 2). 選用映射文件指定的標識符生成器,為持久化對象分配唯一的OID; * 3). 在flush緩存時,會發送一條INSERT語句; * 4). 在使用代理主鍵的情況下,通過setId()方法為臨時對象設置OID是無效的; * 5). 持久化對象的OID不能被隨意修改,因其維持著持久化對象與數據庫記錄的對應關系。 * * Session對象的persist()方法:也會執行INSERT操作; * 與save()方法的不同之處:當臨時對象的OID不為空時,該方法將拋出異常。 */@Testpublic void testSaveAndPersist() { News news = new News(null, "qiaobc", "qiaobei", new Date()); news.setId(1001); // 為臨時對象設置OID是無效的 System.out.println(news); // session.persist(news); session.save(news); System.out.println(news); // news.setId(1002); // 持久化對象的OID不能修改}4.2 get()與load()方法
/** * Session對象的get()與load()方法:均可根據OID從數據庫中加載一個持久化對象 * 1). 執行get()方法時,立即加載對象; * 而執行load()方法時,延遲加載對象,即若不使用該對象,則不會立即查詢,而是返回一個代理對象; * 2). load()方法可能會拋出LazyInitializationException異常:在需要初始化代理對象前關閉Session對象; * 3). 若數據表中沒有與OID對應的記錄,則get()方法返回null; * 而load()方法,若不使用該對象的任何屬性則沒問題,若需要初始化則拋出ObjectNotFoundException異常。 */@Testpublic void testGet() { News news1 = (News) session.get(News.class, 10); System.out.println(news1); // 持久化狀態 session.close(); // 游離狀態 System.out.println(news1);}@Testpublic void testLoad() { News news2 = (News) session.load(News.class, 1); // 若不使用,則返回代理對象com.qiaobc.hibernate.entities.News_$$_javassist_0 System.out.println(news2.getClass().getName()); session.close(); // 游離狀態 System.out.println(news2); // 拋出LazyInitializationException異常}4.3 update()方法
/** * Session對象的update()方法: * 1). 更新持久化對象不需要顯式調用update()方法,因為事務提交時會flush緩存; * 2). 更新游離對象需要顯式調用update()方法,將其轉變為持久化對象; * * 注意: * 1). 無論要更新的游離對象和數據表的記錄是否一致,均會發送UPDATE語句; * 當Hibernate與觸發器協同工作時,可在*.hbm.xml文件的class節點 * 設置select-before-update=true以確保不盲目發送UPDATE語句; * 2). 當 update()方法關聯一個游離對象時,如果在數據庫中不存在相應的記錄,拋出異常; * 3). 當 update()方法關聯一個游離對象時,如果在Session緩存中已經存在相同OID的持久化對象,拋出異常; */@Testpublic void update1() { News news = (News) session.get(News.class, 1); news.setAuthor("SUN"); session.update(news); // 若更新持久化對象不需要顯式調用update()方法}@Testpublic void update2() { News news = (News) session.get(News.class, 1); transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); news.setAuthor("SUN"); // 此時news為游離對象 session.update(news); }4.4 saveOrUpdate()方法
/** * Session對象的saveOrUpdate()方法:臨時對象執行保存操作,游離對象執行更新操作。 * 1). 判定對象為臨時對象的標準:Java對象的OID是否為null; * 2). 了解:若Java對象的OID取值等于映射文件中id節點unsaved-value屬性值,則其也為臨時對象。 */@Testpublic void testSaveOrUpdate() { // OID不為空,執行更新操作;若OID對應的記錄不存在,則拋出StaleStateException異常。 News news1 = new News(10, "qiaobc1", "qiaobei1", new Date()); session.saveOrUpdate(news1); // OID為空,執行保存操作 News news2 = new News(null, "qiaobc", "qiaobei", new Date()); session.saveOrUpdate(news2);}4.5 merge()方法

4.6 delete()方法
/** * Session對象的delete()方法:既可以刪除一個游離對象,也可以刪除一個持久化對象 * 1). 只要OID與數據表中記錄對應,即執行刪除操作;無對應記錄則拋出異常; * 2). 通過設置hibernate.use_identifier_rollback=true,使刪除對象時置OID=null; */@Testpublic void testDelete() { News news = new News(); news.setId(5); session.delete(news); // 刪除游離對象 News news2 = (News) session.get(News.class, 6); session.delete(news2); // 刪除持久化對象 // 配置前:News [id=6, title=qiaobc, author=qiaobei, date=2017-02-20 17:10:35.0] // 配置后:News [id=null, title=qiaobc, author=qiaobei, date=2017-02-20 17:10:35.0] System.out.println(news2);}4.7 evict()方法
/** * Session對象的evict()方法:從緩存中將持久化對象移除 */@Testpublic void testEvict() { News news1 = (News) session.get(News.class, 1); News news2 = (News) session.get(News.class, 2); news1.setAuthor("No.1"); news2.setAuthor("No.2"); session.evict(news2); // 移除news2對象,不再更新該對象}
5. Hibernate調用存儲過程

6. Hibernate與觸發器協同工作
