Hibernate_2 Session及Session API 使用

Session 介紹

  • Session 接口是 Hibernate 向應(yīng)用程序提供的操縱數(shù)據(jù)庫的最主要的接口, 它提供了基本的保存, 更新, 刪除和加載 Java 對(duì)象的方法.

  • Session 具有一個(gè)緩存, 位于緩存中的對(duì)象稱為持久化對(duì)象, 它和數(shù)據(jù)庫中的相關(guān)記錄對(duì)應(yīng). Session 能夠在某些時(shí)間點(diǎn), 按照緩存中對(duì)象的變化來執(zhí)行相關(guān)的 SQL 語句, 來同步更新數(shù)據(jù)庫, 這一過程被稱為刷新緩存(flush)

  • 站在持久化的角度, Hibernate 把對(duì)象分為 4 種狀態(tài):

    • 持久化狀態(tài)
    • 臨時(shí)狀態(tài)
    • 游離狀態(tài)
    • 刪除狀態(tài)

    Session 的特定方法能使對(duì)象從一個(gè)狀態(tài)轉(zhuǎn)換到另一個(gè)狀態(tài).

Session 緩存

  • 在 Session 接口的實(shí)現(xiàn)中包含一系列的 Java 集合, 這些 Java 集合構(gòu)成了 Session 緩存. 只要 Session 實(shí)例沒有結(jié)束生命周期, 且沒有清理緩存,則存放在它緩存中的對(duì)象也不會(huì)結(jié)束生命周期
  • Session 緩存可減少 Hibernate 應(yīng)用程序訪問數(shù)據(jù)庫的頻率

flush 緩存

  • flush:Session 按照緩存中對(duì)象的屬性變化來同步更新數(shù)據(jù)庫
  • 默認(rèn)情況下 Session 在以下時(shí)間點(diǎn)刷新緩存:
    • 顯式調(diào)用 Session 的 flush() 方法
    • 當(dāng)應(yīng)用程序調(diào)用 Transaction 的 commit()方法的時(shí), 該方法先 flush 斋日,然后在向數(shù)據(jù)庫提交事務(wù)
    • 當(dāng)應(yīng)用程序執(zhí)行一些查詢(HQL, Criteria)操作時(shí),如果緩存中持久化對(duì)象的屬性已經(jīng)發(fā)生了變化寥枝,會(huì)先 flush 緩存,以保證查詢結(jié)果能夠反映持久化對(duì)象的最新狀態(tài)
  • flush 緩存的例外情況: 如果對(duì)象使用 native 生成器生成 OID, 那么當(dāng)調(diào)用 Session 的 save() 方法保存對(duì)象時(shí), 會(huì)立即執(zhí)行向數(shù)據(jù)庫插入該實(shí)體的 insert 語句.
  • commit() 和 flush() 方法的區(qū)別:flush 執(zhí)行一系列 sql 語句将塑,但不提交事務(wù)脉顿;commit 方法先調(diào)用flush() 方法,然后提交事務(wù). 意味著提交事務(wù)意味著對(duì)數(shù)據(jù)庫操作永久保存下來点寥。

設(shè)定刷新緩存的時(shí)間點(diǎn)

若希望改變 flush 的默認(rèn)時(shí)間點(diǎn), 可以通過 Session 的 setFlushMode() 方法顯式設(shè)定 flush 的時(shí)間點(diǎn)

清理緩存的模式 各種查詢方法 Transaction 的 commit() 方法 Session 的 flush() 方法
FlushMode.AUTO(默認(rèn)模式) 清理 清理 清理
FlushMode.COMMIT 不清理 清理 清理
FlushMode.NEVER 不清理 不清理 清理

refresh()

會(huì)強(qiáng)制發(fā)送 SELECT 語句, 以使 Session 緩存中對(duì)象的狀態(tài)和數(shù)據(jù)表中對(duì)應(yīng)的記錄保持一致!

@Test
public void testRefresh() {
    Student student = session.get(Student.class, 1);
    System.out.println(student);
    //在此處斷點(diǎn)艾疟, 然后修改數(shù)據(jù)庫中數(shù)據(jù),向下執(zhí)行
    session.refresh(student);
    System.out.println(student);
}

持久化對(duì)象的狀態(tài)

  • 臨時(shí)對(duì)象(Transient):
    • 在使用代理主鍵的情況下, OID 通常為 null
    • 不處于 Session 的緩存中
    • 在數(shù)據(jù)庫中沒有對(duì)應(yīng)的記錄
  • 持久化對(duì)象(也叫”托管”)(Persist):
    • OID 不為 null
    • 位于 Session 緩存中
    • 若在數(shù)據(jù)庫中已經(jīng)有和其對(duì)應(yīng)的記錄, 持久化對(duì)象和數(shù)據(jù)庫中的相關(guān)記錄對(duì)應(yīng)
    • Session 在 flush 緩存時(shí), 會(huì)根據(jù)持久化對(duì)象的屬性變化, 來同步更新數(shù)據(jù)庫
    • 在同一個(gè) Session 實(shí)例的緩存中, 數(shù)據(jù)庫表中的每條記錄只對(duì)應(yīng)唯一的持久化對(duì)象
  • 刪除對(duì)象(Removed)
    • 在數(shù)據(jù)庫中沒有和其 OID 對(duì)應(yīng)的記錄
    • 不再處于 Session 緩存中
    • 一般情況下, 應(yīng)用程序不該再使用被刪除的對(duì)象
  • 游離對(duì)象(也叫”脫管”) (Detached):
    • OID 不為 null
    • 不再處于 Session 緩存中
    • 一般情況需下, 游離對(duì)象是由持久化對(duì)象轉(zhuǎn)變過來的, 因此在數(shù)據(jù)庫中可能還存在與它對(duì)應(yīng)的記錄
  • 對(duì)象的狀態(tài)轉(zhuǎn)換圖


    對(duì)象的狀態(tài)轉(zhuǎn)換圖

Session API 使用

Save() 方法

  • Session 的 save() 方法使一個(gè)臨時(shí)對(duì)象轉(zhuǎn)變?yōu)槌志没瘜?duì)象
  • Session 的 save() 方法完成以下操作:
    • 把映射對(duì)象加入到 Session 緩存中, 使它進(jìn)入持久化狀態(tài)
    • 選用映射文件指定的標(biāo)識(shí)符生成器, 為持久化對(duì)象分配唯一的 OID. 在使用代理主鍵的情況下, setId() 方法為映射對(duì)象設(shè)置 OID 是無效的.
    • 計(jì)劃執(zhí)行一條 insert 語句:在 flush 緩存的時(shí)候
  • Hibernate 通過持久化對(duì)象的 OID 來維持它和數(shù)據(jù)庫相關(guān)記錄的對(duì)應(yīng)關(guān)系. 當(dāng)映射對(duì)象處于持久化狀態(tài)時(shí), 不允許程序隨意修改它的 ID
  • persist() 和 save() 區(qū)別:
    • 當(dāng)對(duì)一個(gè) OID 不為 Null 的對(duì)象執(zhí)行 save() 方法時(shí), 會(huì)把該對(duì)象以一個(gè)新的 oid 保存到數(shù)據(jù)庫中; 但執(zhí)行 persist() 方法時(shí)會(huì)拋出一個(gè)異常.
/**
 * 1. save() 方法
 * 1). 使一個(gè)臨時(shí)對(duì)象變?yōu)槌志没瘜?duì)象
 * 2). 為對(duì)象分配 ID.
 * 3). 在 flush 緩存時(shí)會(huì)發(fā)送一條 INSERT 語句.
 * 4). 在 save 方法之前的 id 是無效的
 * 5). 持久化對(duì)象的 ID 是不能被修改的!
 */
@Test
public void testSave() {
    Student student = new Student("小明", 56, new Date(new java.util.Date().getTime()));
    session.save(student);
//  student.setId(200);
}
/**
 * persist(): 也會(huì)執(zhí)行 INSERT 操作
 * 和 save() 的區(qū)別 : 
 * 在調(diào)用 persist 方法之前, 若對(duì)象已經(jīng)有 id 了, 則不會(huì)執(zhí)行 INSERT, 而拋出異常
 */
@Test
public void testPersist() {
    Student student = new Student("小明", 57, new Date(new java.util.Date().getTime()));
    //student.setId(35);
    session.persist(student);
}

get() 和 load() 方法

  • 都可以根據(jù)跟定的 OID 從數(shù)據(jù)庫中加載一個(gè)持久化對(duì)象
  • 區(qū)別
    • 執(zhí)行 get 方法: 會(huì)立即加載對(duì)象. 執(zhí)行 load 方法, 若不適用該對(duì)象, 則不會(huì)立即執(zhí)行查詢操作, 而返回一個(gè)代理對(duì)象敢辩,get 是 立即檢索, load 是延遲檢索.
    • 在需要初始化代理對(duì)象之前已經(jīng)關(guān)閉了 Session 蔽莱, load 方法可能會(huì)拋出 LazyInitializationException 異常。
    • 若數(shù)據(jù)表中沒有對(duì)應(yīng)的記錄, Session 也沒有被關(guān)閉.get 返回 null戚长,load 若不使用該對(duì)象的任何屬性, 沒問題; 若一旦使用該對(duì)象盗冷,就會(huì)去獲取對(duì)象, 就會(huì)拋出異常(ObjectNotFoundException ).
@Test
public void testGet() {
    //get 獲取對(duì)象時(shí)立即加載
    Student student = session.get(Student.class, 1);
    System.out.println(student.getName());
}

@Test
public void testLoad() {
    //load 獲取對(duì)象時(shí),等到使用時(shí)才會(huì)加載同廉,如果使用時(shí)加載的對(duì)象不存在仪糖,就會(huì)拋出 ObjectNotFoundException 異常
    Student student = session.load(Student.class, 100);
    System.out.println(student.getName());
}

update() 方法

  • Session 的 update() 方法使一個(gè)游離對(duì)象轉(zhuǎn)變?yōu)槌志没瘜?duì)象, 并且計(jì)劃執(zhí)行一條 update 語句.
  • 若更新一個(gè)持久化對(duì)象, 不需要顯示的調(diào)用 update 方法. 因?yàn)樵谡{(diào)用 Transaction的 commit() 方法時(shí), 會(huì)先執(zhí)行 session 的 flush 方法.
  • 若希望 Session 僅當(dāng)修改了 News 對(duì)象的屬性時(shí), 才執(zhí)行 update() 語句, 可以把映射文件中 <class> 元素的 select-before-update 設(shè)為 true. 該屬性的默認(rèn)值為 false
  • 當(dāng) update() 方法關(guān)聯(lián)一個(gè)游離對(duì)象時(shí), 如果在 Session 的緩存中已經(jīng)存在相同 OID 的持久化對(duì)象, 會(huì)拋出異常
  • 當(dāng) update() 方法關(guān)聯(lián)一個(gè)游離對(duì)象時(shí), 如果在數(shù)據(jù)庫中不存在相應(yīng)的記錄, 也會(huì)拋出異常.
<class name="com.cfox.hibernate.Student" table="STUDENT_INFO" select-before-update="true">
/**
 * update:
 * 1. 若更新一個(gè)持久化對(duì)象, 不需要顯示的調(diào)用 update 方法. 因?yàn)樵谡{(diào)用 Transaction
 * 的 commit() 方法時(shí), 會(huì)先執(zhí)行 session 的 flush 方法.
 * 2. 更新一個(gè)游離對(duì)象, 需要顯式的調(diào)用 session 的 update 方法. 可以把一個(gè)游離對(duì)象
 * 變?yōu)槌志没瘜?duì)象
 * 
 * 需要注意的:
 * 1. 無論要更新的游離對(duì)象和數(shù)據(jù)表的記錄是否一致, 都會(huì)發(fā)送 UPDATE 語句. 
 *    如何能讓 updat 方法不再盲目的出發(fā) update 語句呢 ? 在 .hbm.xml 文件的 class 節(jié)點(diǎn)設(shè)置
 *    select-before-update=true (默認(rèn)為 false). 但通常不需要設(shè)置該屬性. 
 * 
 * 2. 若數(shù)據(jù)表中沒有對(duì)應(yīng)的記錄, 但還調(diào)用了 update 方法, 會(huì)拋出異常
 * 
 * 3. 當(dāng) update() 方法關(guān)聯(lián)一個(gè)游離對(duì)象時(shí), 
 * 如果在 Session 的緩存中已經(jīng)存在相同 OID 的持久化對(duì)象, 會(huì)拋出異常. 因?yàn)樵?Session 緩存中
 * 不能有兩個(gè) OID 相同的對(duì)象!
 *    
 */
@Test
public void testUpdate() {
    
    Student student = session.get(Student.class, 1);
    student.setName("AAA");
    session.close();
    session = sessionFactory.openSession();
    Student student1 = session.get(Student.class, 1);
    session.update(student);
}

saveOrUpdate() 方法

  • Session 的 saveOrUpdate() 方法同時(shí)包含了 save() 與 update() 方法的功能
  • 執(zhí)行流程圖:
執(zhí)行流程圖
  • 判定對(duì)象為臨時(shí)對(duì)象的標(biāo)準(zhǔn)

    • Java 對(duì)象的 OID 為 null
    • 映射文件中為 <id> 設(shè)置了 unsaved-value 屬性, 并且 Java 對(duì)象的 OID 取值與這個(gè) unsaved-value 屬性值匹配
    <id name="id" type="java.lang.Integer" unsaved-value="12">
    
@Test
public void testSaveOrUpdate() {
    Student student = new Student("小li", 35, new java.util.Date());
    session.saveOrUpdate(student);
}

delete() 方法

  • Session 的 delete() 方法既可以刪除一個(gè)游離對(duì)象, 也可以刪除一個(gè)持久化對(duì)象

  • Session 的 delete() 方法處理過程

    • 計(jì)劃執(zhí)行一條 delete 語句
    • 把對(duì)象從 Session 緩存中刪除, 該對(duì)象進(jìn)入刪除狀態(tài).
  • Hibernate 的 cfg.xml 配置文件中有一個(gè) hibernate.use_identifier_rollback 屬性, 其默認(rèn)值為 false, 若把它設(shè)為 true, 將改變 delete() 方法的運(yùn)行行為: delete() 方法會(huì)把持久化對(duì)象或游離對(duì)象的 OID 設(shè)置為 null, 使它們變?yōu)榕R時(shí)對(duì)象

    <property name="hibernate.use_identifier_rollback">true</property>
    
/**
 * delete: 執(zhí)行刪除操作. 只要 OID 和數(shù)據(jù)表中一條記錄對(duì)應(yīng), 就會(huì)準(zhǔn)備執(zhí)行 delete 操作
 * 若 OID 在數(shù)據(jù)表中沒有對(duì)應(yīng)的記錄, 則拋出異常
 * 
 * 可以通過設(shè)置 hibernate 配置文件 hibernate.use_identifier_rollback 為 true,
 * 使刪除對(duì)象后, 把其 OID 置為  null
 */
@Test
public void testDelete() {
    Student student = session.get(Student.class, 2);
    session.delete(student);
    
    System.out.println(student.toString());
}

doWork(Work) 方法

通過 Hibernate 調(diào)用存儲(chǔ)過程

  • Work 接口: 直接通過 JDBC API 來訪問數(shù)據(jù)庫的操作
public interface Work {
    void execute(Connection connection) throws SQLException;
}
  • Session 的 doWork(Work) 方法用于執(zhí)行 Work 對(duì)象指定的操作, 即調(diào)用 Work 對(duì)象的 execute() 方法. Session 會(huì)把當(dāng)前使用的數(shù)據(jù)庫連接傳遞給 execute() 方法.
@Test
public void testDoWork() {
    Work work = new Work() {
        // 通過jdbc API 來訪問數(shù)據(jù)庫
        @Override
        public void execute(Connection connection) throws SQLException {
            System.out.println(connection); 
        }
    };
    session.doWork(work);
}

Hibernate 與觸發(fā)器協(xié)同工作

  • Hibernate 與數(shù)據(jù)庫中的觸發(fā)器協(xié)同工作時(shí), 會(huì)造成兩類問題
    觸發(fā)器使 Session 的緩存中的持久化對(duì)象與數(shù)據(jù)庫中對(duì)應(yīng)的數(shù)據(jù)不一致:觸發(fā)器運(yùn)行在數(shù)據(jù)庫中, 它執(zhí)行的操作對(duì) Session 是透明的
  • Session 的 update() 方法盲目地激發(fā)觸發(fā)器: 無論游離對(duì)象的屬性是否發(fā)生變化, 都會(huì)執(zhí)行 update 語句, 而 update 語句會(huì)激發(fā)數(shù)據(jù)庫中相應(yīng)的觸發(fā)器
    解決方案:
    • 在執(zhí)行完 Session 的相關(guān)操作后, 立即調(diào)用 Session 的 flush() 和 refresh() 方法, 迫使 Session
    • 的緩存與數(shù)據(jù)庫同步(refresh() 方法重新從數(shù)據(jù)庫中加載對(duì)象)
  • 在映射文件的的 <class> 元素中設(shè)置 select-before-update 屬性: 當(dāng) Session 的 update 或 saveOrUpdate() 方法更新一個(gè)游離對(duì)象時(shí), 會(huì)先執(zhí)行 Select 語句, 獲得當(dāng)前游離對(duì)象在數(shù)據(jù)庫中的最新數(shù)據(jù), 只有在不一致的情況下才會(huì)執(zhí)行 update 語句
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市迫肖,隨后出現(xiàn)的幾起案子锅劝,更是在濱河造成了極大的恐慌,老刑警劉巖蟆湖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件故爵,死亡現(xiàn)場離奇詭異,居然都是意外死亡隅津,警方通過查閱死者的電腦和手機(jī)诬垂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伦仍,“玉大人结窘,你說我怎么就攤上這事〕淅叮” “怎么了晦鞋?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我悠垛,道長,這世上最難降的妖魔是什么娜谊? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任确买,我火速辦了婚禮,結(jié)果婚禮上纱皆,老公的妹妹穿的比我還像新娘湾趾。我一直安慰自己,他們只是感情好派草,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布搀缠。 她就那樣靜靜地躺著,像睡著了一般近迁。 火紅的嫁衣襯著肌膚如雪艺普。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天鉴竭,我揣著相機(jī)與錄音歧譬,去河邊找鬼。 笑死搏存,一個(gè)胖子當(dāng)著我的面吹牛瑰步,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播璧眠,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼缩焦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了责静?” 一聲冷哼從身側(cè)響起袁滥,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泰演,沒想到半個(gè)月后呻拌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡睦焕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年藐握,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垃喊。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猾普,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出本谜,到底是詐尸還是另有隱情初家,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站溜在,受9級(jí)特大地震影響陌知,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜掖肋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一仆葡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧志笼,春花似錦沿盅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至紊浩,卻和暖如春窖铡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背郎楼。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國打工万伤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呜袁。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓敌买,卻偏偏與公主長得像,于是被迫代替她去往敵國和親阶界。 傳聞我的和親對(duì)象是個(gè)殘疾皇子虹钮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容