深入學(xué)習(xí)Hibernate持久化對(duì)象的三個(gè)狀態(tài)
Hibernate中的對(duì)象有3中狀態(tài),瞬時(shí)對(duì)象(TransientObjects)、持久化對(duì)象(PersistentObjects)和離線對(duì)象(DetachedObjects也叫做脫管對(duì)象)。
下圖3.1顯示了瞬時(shí)對(duì)象、持久化對(duì)象和離線對(duì)象之間的關(guān)系以及它們之間的轉(zhuǎn)換。
圖3.1
臨時(shí)狀態(tài):由Java的new命令開辟內(nèi)存空間的java對(duì)象也就是普通的java對(duì)象,如果沒有變量引用它它將會(huì)被JVM收回。臨時(shí)對(duì)象在內(nèi)存中是孤立存在的,它的意義是攜帶信息載體,不和數(shù)據(jù)庫(kù)中的數(shù)據(jù)由任何的關(guān)聯(lián)。通過Session的save()方法和saveOrUpdate()方法可以把一個(gè)臨時(shí)對(duì)象和數(shù)據(jù)庫(kù)相關(guān)聯(lián),并把臨時(shí)對(duì)象攜帶的信息通過配置文件所做的映射插入數(shù)據(jù)庫(kù)中,這個(gè)臨時(shí)對(duì)象就成為持久化對(duì)象。
持久化狀態(tài):持久化對(duì)象在數(shù)據(jù)庫(kù)中有相應(yīng)的記錄,持久化對(duì)象可以是剛被保存的,或者剛被加載的,但都是在相關(guān)聯(lián)的session聲明周期中保存這個(gè)狀態(tài)。如果是直接數(shù)據(jù)庫(kù)查詢所返回的數(shù)據(jù)對(duì)象,則這些對(duì)象和數(shù)據(jù)庫(kù)中的字段相關(guān)聯(lián),具有相同的id,它們馬上變成持久化對(duì)象。如果一個(gè)臨時(shí)對(duì)象被持久化對(duì)象引用,也立馬變?yōu)槌志没瘜?duì)象。
如果使用delete()方法,持久化對(duì)象變?yōu)榕R時(shí)對(duì)象,并且刪除數(shù)據(jù)庫(kù)中相應(yīng)的記錄,這個(gè)對(duì)象不再與數(shù)據(jù)庫(kù)有任何的聯(lián)系。
持久化對(duì)象總是與Session和Transaction關(guān)聯(lián)在一起,在一個(gè)session中,對(duì)持久化對(duì)象的操作不會(huì)立即寫到數(shù)據(jù)庫(kù),只有當(dāng)Transaction(事務(wù))結(jié)束時(shí),才真正的對(duì)數(shù)據(jù)庫(kù)更新,從而完成持久化對(duì)象和數(shù)據(jù)庫(kù)的同步。在同步之前的持久化對(duì)象成為臟對(duì)象。
當(dāng)一個(gè)session()執(zhí)行close()、clear()、或evict()之后,持久化對(duì)象就變?yōu)殡x線對(duì)象,這時(shí)對(duì)象的id雖然擁有數(shù)據(jù)庫(kù)的識(shí)別值,但已經(jīng)不在Hibernate持久層的管理下,他和臨時(shí)對(duì)象基本上一樣的,只不過比臨時(shí)對(duì)象多了數(shù)據(jù)庫(kù)標(biāo)識(shí)id。沒有任何變量引用時(shí),jvm將其回收。
脫管狀態(tài):Session關(guān)閉之后,與此Session關(guān)聯(lián)的持久化對(duì)象就變成為脫管對(duì)象,可以繼續(xù)對(duì)這個(gè)對(duì)象進(jìn)行修改,如果脫管對(duì)象被重新關(guān)聯(lián)到某個(gè)新的Session上,會(huì)在此轉(zhuǎn)成持久對(duì)象。
脫管對(duì)象雖然擁有用戶的標(biāo)識(shí)id,所以通過update()、saveOrUpdate()等方法,再次與持久層關(guān)聯(lián)。
下面我們就通過使用hibernate,實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的增刪改查來體現(xiàn)三種狀態(tài)之間的轉(zhuǎn)換過程。
添加修改演示三種狀態(tài)之間的變化
當(dāng)我們建立Session都要實(shí)例化SessionFactory,所以我們把重復(fù)的代碼進(jìn)行封裝,并且session是單線程的。我們把對(duì)session的管理,打開session,關(guān)閉session等封裝到工具類中,代碼如下所示。
package com.bjpowernode.hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtils { private static SessionFactory factory; //static只初始化一次. static { try{ //默認(rèn)讀取的是hibernate.cfg.xml 文件. Configuration cfg = new Configuration().configure(); //建立SessionFactory. factory = cfg.buildSessionFactory(); }catch(Exception e ) { e.printStackTrace(); } } public static Session getSession() { //打開session. return factory.openSession(); } //關(guān)閉session. public static void closeSession(Session session) { //判斷是否為空. //判斷是否是打開狀態(tài)再進(jìn)行關(guān)閉. if(session!=null) { if(session.isOpen()) { session.close(); } } } //返回工廠類. public static SessionFactory getSessionFactory() { return factory; } }
Hibernate.cfg.xml代碼如下所示。
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory > <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/Hibernate_session</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.show_sql">true</property> <mapping resource="com/bjpowernode/hibernate/User.hbm.xml"/> </session-factory> </hibernate-configuration>
之前我們把對(duì)表添加的操作放到普通的java類中,在這個(gè)類的main()方法中執(zhí)行,如果我們?cè)賹?duì)表進(jìn)行其他的操作呢?那是不是還要建立新的java類,多個(gè)方法就不容易測(cè)試了。我們使用測(cè)試工具類JUnit來做測(cè)試,來測(cè)試增刪改查。首先建立源目錄,在test包中放測(cè)試程序。
我們建立我們的測(cè)試程序SessionTest.java,繼承TestCase類,這樣我們?cè)赟essionTest.java類中測(cè)試數(shù)據(jù)庫(kù)中的某個(gè)方法,方法名的規(guī)范要以test開頭。我們向User表中添加一條記錄如下代碼所示。
package com.bjpowernode.hibernate; import java.util.Date; import junit.framework.TestCase; import org.hibernate.Session; import org.hibernate.Transaction; public class SessionTest extends TestCase { //測(cè)試方法以test開頭. public void testSave1() { Session session = null; Transaction tx = null; try { //取得session. session = HibernateUtils.getSession(); //自己開啟事務(wù). 返回 transient的一個(gè)實(shí)例. tx = session.beginTransaction(); //傳入值.變?yōu)門ransient狀態(tài). User user = new User(); user.setName("張三"); user.setPassword("123"); user.setCreateTime(new Date()); user.setExpireTime(new Date()); //進(jìn)行保存.執(zhí)行save則對(duì)session進(jìn)行管理了. 處于持久狀態(tài). //persistent狀態(tài)的對(duì)象.當(dāng)對(duì)象的屬性發(fā)生改變的時(shí)候,hibernate在清理 //緩存的時(shí)候(臟數(shù)據(jù)檢查)的時(shí)候,會(huì)和數(shù)據(jù)庫(kù)同步. session.save(user); user.setName("李四"); //再次提交. tx.commit(); }catch(Exception e) { e.printStackTrace(); if(tx!=null) { // 事務(wù)回滾. tx.rollback(); } }finally { //關(guān)閉session.當(dāng)關(guān)閉session時(shí)處于Detached狀態(tài). HibernateUtils.closeSession(session); } }
首先是建立對(duì)象與表的會(huì)話session,開啟事務(wù)session.beginTransaction(),實(shí)例化User對(duì)象,當(dāng)我們User user = new User()的時(shí)候,當(dāng)我們new一個(gè)對(duì)象的時(shí)候數(shù)據(jù)庫(kù)是沒有的,所以是Transient對(duì)象(臨時(shí)對(duì)象),然后給user賦值,設(shè)置名稱和密碼以及其他屬性。 為對(duì)象的所有屬性賦值完畢,接下來保存到會(huì)話session中,拿到session執(zhí)行save(user)方法。 當(dāng)我們執(zhí)行session的save()方法時(shí),這個(gè)對(duì)象就被session管理了,session中有個(gè)map,會(huì)把對(duì)象放到map中,此時(shí)的對(duì)象我們就成為persistent狀態(tài)(持久狀態(tài))。
接下來我們又把user中的name屬性設(shè)置為“李四”,之后提交事務(wù)。我們先再會(huì)話中存儲(chǔ)的“張三”,之后改為“李四”。try catch來?yè)渥疆惓#?dāng)執(zhí)行完畢,關(guān)閉session后,對(duì)象處于detached狀態(tài)(離線狀態(tài))。
我們創(chuàng)建數(shù)據(jù)庫(kù),利用ExportDB.java方法建立表。之后執(zhí)行SessionTest的testSave1()方法,當(dāng)執(zhí)行到session方法的時(shí)候,表中自動(dòng)生成user表的id值,并且名子為“張三”,之后再次執(zhí)行,名字又變?yōu)椤袄钏摹?,之后?zhí)行事務(wù)的commit()方法tx .commit ,此時(shí)控制臺(tái)才發(fā)出語(yǔ)句,如下圖3.2。
從控制臺(tái)的語(yǔ)句中可以看出,顯示發(fā)送的插入sql語(yǔ)句,后是update語(yǔ)句,首先是持久化對(duì)象user中的名字為“張三”,所以save的時(shí)候生成inset語(yǔ)句。此時(shí)user處于持久狀態(tài)的對(duì)象,我們之后又給變了持久化對(duì)象,所以發(fā)送了一個(gè)修改語(yǔ)句。也就是當(dāng)持久化對(duì)象發(fā)生修改時(shí),我們?cè)偬峤皇聞?wù),就會(huì)把修改的全部體現(xiàn)出來(update語(yǔ)句)。
也就是我們?cè)偬峤皇聞?wù)的時(shí)候,在清理緩存,也就是臟數(shù)據(jù)檢查(內(nèi)存中變了,而數(shù)據(jù)沒變),要檢查哪些數(shù)據(jù)是有問題的,要保持內(nèi)存和數(shù)據(jù)庫(kù)的同步。所以我們數(shù)據(jù)庫(kù)中添加的記錄,user的名字為李四(如圖3.3所示)。
圖3.3
如果上述代碼中,我們?cè)谛薷拿譃槔钏暮髐ser.setName("李四");我們顯示調(diào)用session的update()方法,session.update(),運(yùn)行,會(huì)看到控制臺(tái)上打印的sql語(yǔ)句和我們不加如session.update()打印的相同。持久化對(duì)象只要更改了,在提交事務(wù)的時(shí)候就會(huì)同步,沒有必要再顯示調(diào)用。
Detached狀態(tài)演示
我們?cè)趫?zhí)行完所有的操作,關(guān)閉session后,此時(shí)的user對(duì)象變?yōu)閐etached狀態(tài),此時(shí)進(jìn)行操作。
代碼如下所示。
public void testSave3() { Session session = null; Transaction tx = null; User user = null; try { //取得session. session = HibernateUtils.getSession(); //自己開啟事務(wù). fanhui transient的一個(gè)實(shí)例. tx = session.beginTransaction(); //傳入值.變?yōu)門ransient狀態(tài). user = new User(); user.setName("張三"); user.setPassword("123"); user.setCreateTime(new Date()); user.setExpireTime(new Date()); //進(jìn)行保存.執(zhí)行save則對(duì)session進(jìn)行管理了. 處于持久狀態(tài). //persistent狀態(tài)的對(duì)象.當(dāng)對(duì)象的屬性發(fā)生改變的時(shí)候,hibernate在清理 //緩存的時(shí)候(臟數(shù)據(jù)檢查)的時(shí)候,會(huì)和數(shù)據(jù)庫(kù)同步. session.save(user); user.setName("李四"); //可以顯示的調(diào)用update方法,因?yàn)榇藭r(shí)為持久狀態(tài),調(diào)用update沒有什么意義. //再次提交. tx.commit(); }catch(Exception e) { e.printStackTrace(); if(tx!=null) { // 事務(wù)回滾. tx.rollback(); } }finally { //關(guān)閉session.當(dāng)關(guān)閉session時(shí)處于Detached狀態(tài). HibernateUtils.closeSession(session); } //已經(jīng)不能用以前的session了. user.setName("王五"); try { //得到新的session. session = HibernateUtils.getSession(); //開啟事務(wù). session.beginTransaction(); //將detached狀態(tài)的對(duì)象重新納入session管理. //此時(shí)將變?yōu)閜ersistent狀態(tài)的對(duì)象. //persistent狀態(tài)的對(duì)象,在清理緩存時(shí),會(huì)根數(shù)據(jù)庫(kù)同步. session.update(user); //提交事務(wù).把內(nèi)存的改變提交到數(shù)據(jù)庫(kù)上. session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
取得detached狀態(tài)的user對(duì)象,改變這個(gè)對(duì)象的name值,user.setName("王五");之后我們?cè)賜ew一個(gè)新的
session,通過session開啟事務(wù),之后更新操作,session.update(user),也就是把離線的對(duì)象(或脫管對(duì)象)再納入session管理,這樣就會(huì)和數(shù)據(jù)庫(kù)同步,因?yàn)閟ession.update()就把user對(duì)象納入session管理,user對(duì)象由離線狀態(tài)變?yōu)閜ersistent狀態(tài)。
提交事務(wù),將和數(shù)據(jù)庫(kù)同步。把內(nèi)存的改變體現(xiàn)到數(shù)據(jù)庫(kù)上??刂婆_(tái)sql語(yǔ)句以及運(yùn)行向表中添加記錄結(jié)果如圖3.4,3.5所示。
圖3.4
總結(jié)
以上所述是小編給大家介紹的Hibernate持久化對(duì)象的三個(gè)狀態(tài),希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Java中Optional.of()方法及源碼解析(非常詳細(xì)!)
這篇文章主要給大家介紹了關(guān)于Java中Optional.of()方法及源碼解析的相關(guān)資料,Java中java.util .Optional類的of()方法用于獲得該Optional類中具有指定類型的指定值的一個(gè)實(shí)例,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06SpringCloud Ribbon負(fù)載均衡代碼實(shí)例
這篇文章主要介紹了SpringCloud Ribbon負(fù)載均衡代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03springboot多數(shù)據(jù)源配合docker部署mysql主從實(shí)現(xiàn)讀寫分離效果
這篇文章主要介紹了springboot多數(shù)據(jù)源配合docker部署mysql主從實(shí)現(xiàn)讀寫分離,通過使用docker獲取mysql鏡像,具體內(nèi)容詳情跟隨小編一起看看吧2021-09-09SpringBoot實(shí)現(xiàn)自定義事件的方法詳解
這篇文章將用實(shí)例來和大家介紹一下如何在SpringBoot中自定義事件來使用觀察者模式。文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)SpringBoot有一定的幫助,需要的可以參考一下2022-06-06maven-surefire-plugin總結(jié)示例詳解
這篇文章主要介紹了maven-surefire-plugin總結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07SpringBoot如何實(shí)現(xiàn)Tomcat自動(dòng)配置
這篇文章主要介紹了SpringBoot如何實(shí)現(xiàn)Tomcat自動(dòng)配置,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot框架,感興趣的朋友可以了解下2021-03-03基于Spring Batch向Elasticsearch批量導(dǎo)入數(shù)據(jù)示例
本文介紹了基于Spring Batch向Elasticsearch批量導(dǎo)入數(shù)據(jù)示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02