瞬時對象(Transient Objects):使用new操作符初始化的對象不是立刻就持久化的。他們的狀態是瞬時的,也就是說他們沒有任何跟數據庫表相關聯的行為,只要應用不再引用這些對象(不再被任何其他對象所引用),他們的狀態將會丟失,并由垃圾回收機制回收。
臨時對象的特征。臨時對象具有以下特征:(1) 不處于session的緩存中,也可以說,不被任何一個Session實例關聯。(2) 在數據庫中沒有對應的記錄。在以下情況下,java對象進入臨時狀態:(1) 當通過new語句剛創建了一個Java對象,它處于臨時狀態,此時不和數據庫中的任何記錄對應。(2) Session的delete()方法能使一個持久化對象或游離對象轉變為臨時對象。對于游離對象,delete()方法從數據庫中刪除與它對應的記錄;對于持久化對象,delete()方法從數據庫中刪除與它對應的記錄,并且把它從Session的緩存中刪除。
持久化對象(Persist Objects):持久實例是任何具有數據庫標識的實例。它有持久化管理器Session統一管理,持久實例是在事務中進行操作的———他們的狀態在事務結束時同數據庫進行同步。當事務提交時,通過執行SQL的INSERT,UPDATE和DELETE語句把內存中的狀態同步到數據庫中。
持久化對象的特征。持久化對象具有以下特征:(1) 位于一個Session實例的緩存中,也可以說,持久化對象總是被一個Session實例關聯。(2) 持久化對象和數據庫中的相關記錄對應。(3) Session在清理緩存時,會根據持久化對象的屬性變化,來同步更新數據庫。Session的許多方法都能夠觸發Java對象進入持久化狀態:(1) Session的save()方法把臨時對象轉變為持久化對象。(2) Session的load()或get()方法返回的對象總是處于持久化狀態。(3) Session的find()方法返回的List集合中存放的都是持久化對象。(4) Session的update()、saveOrUpdate()和lock()方法使游離對象轉變為持久化對象。(5)當一個持久化對象關聯一個臨時對象,在允許級聯保存的情況下,Session在清理緩存時會把這個臨時對象也轉變為持久化對象。hibernate保證在同一個Session實例的緩存中,數據庫表中的每條記錄只對應惟一的持久化對象。例如對于以下代碼,共創建了兩個Session實例:session1和session2。session1和session2擁有各自的緩存。在session1的緩存中,只會有惟一的OID為1的Customer持久化對象,在session2的緩存中,也只會有惟一的OID為2的Customer持久化對象。因此在內存中共有兩個Customer持久化對象,一個屬于session1的緩存,一個屬于session2的緩存。引用變量a和b都引用session1緩存中的Customer持久化對象,而引用變量c引用session2緩存中的Customer持久化對象:Session session1=sessionFactory.openSession();Session session2=sessionFactory.openSession();Transaction tx1 = session1.beginTransaction();Transaction tx2 = session2.beginTransaction();
Customer a=(Customer)session1.load(Customer.class,new Long(1));Customer b=(Customer)session1.load(Customer.class,new Long(1));Customer c=(Customer)session2.load(Customer.class,new Long(1));
System.out.PRintln(a= =b); //trueSystem.out.println(a= =c); //false
tx1.commit();tx2.commit();session1.close();session2.close();Java對象的持久化狀態是相對于某個具體的Session實例的,以下代碼試圖使一個Java對象同時被兩個Session實例關聯:Session session1=sessionFactory.openSession();Session session2=sessionFactory.openSession();Transaction tx1 = session1.beginTransaction();Transaction tx2 = session2.beginTransaction();
Customer c=(Customer)session1.load(Customer.class,new Long(1)); //Customer對象被session1關聯session2.update(c); //Customer對象被session2關聯c.setName("Jack"); //修改Customer對象的屬性
tx1.commit(); //執行update語句tx2.commit(); //執行update語句session1.close();session2.close();
當執行session1的load()方法時,OID為1的Customer對象被加入到session1的緩存中,因此它是session1的持久化對象,此時它還沒有被session2關聯,因此相對于session2,它處于游離狀態。當執行session2的update()方法時,Customer對象被加入到session2的緩存中,因此也成為session2的持久化對象。接下來修改Customer對象的name屬性,會導致兩個Session實例在清理各自的緩存時,都執行相同的update語句:update CUSTOMERS set NAME='Jack' …… where ID=1;在實際應用程序中,應該避免一個Java對象同時被多個Session實例關聯,因為這會導致重復執行SQL語句,并且極容易出現一些并發問題。
離線對象(Detached Objects):Session關閉之后,持久化對象就變為離線對象。離線表示這個對象不能再與數據庫保持同步,他們不再受Hibernate管理。
游離對象的特征。游離對象具有以下特征:(1) 不再位于Session的緩存中,也可以說,游離對象不被Session關聯。(2) 游離對象是由持久化對象轉變過來的,因此在數據庫中可能還存在與它對應的記錄(前提條件是沒有其他程序刪除了這條記錄)。游離對象與臨時對象的相同之處在于,兩者都不被Session關聯,因此Hibernate不會保證它們的屬性變化與數據庫保持同步。游離對象與臨時對象的區別在于:前者是由持久化對象轉變過來的,因此可能在數據庫中還存在對應的記錄,而后者在數據庫中沒有對應的記錄。Session的以下方法使持久化對象轉變為游離對象:(1) 當調用Session的close()方法時,Session的緩存被清空,緩存中的所有持久化對象都變為游離對象。如果在應用程序中沒有引用變量引用這些游離對象,它們就會結束生命周期。(2)Session的evict()方法能夠從緩存中刪除一個持久化對象,使它變為游離狀態。當Session的緩存中保存了大量的持久化對象,會消耗許多內存空間,為了提高性能,可以考慮調用evict()方法,從緩存中刪除一些持久化對象。但是多數情況下不推薦使用evict()方法,而應該通過查詢語言,或者顯式的導航來控制對象圖的深度。
Java對象在JVM中的生命周期:
創建一個Java對象時,JVM會為這個對象分配一個內存空間,只要這個對象被引用變量引用,就一直存在于內存中,如果一個對象不被任何引用變量引用,就結束生命周期。Java集合(List、Map、Set)存放的是Java對象的引用,當向集合中添加一個對象時,其實是把這個對象的引用添加到集合中。因此集合中含有的對象生命周期一直存在。
理解Session的緩存:
在Session接口的實現中定義一系列的Java集合,這些集合構成了Session的緩存。
Session緩存的作用:1、減少訪問數據庫的頻率,可以提高數據庫訪問的性能。2、保證緩存中的對象與數據庫中的相關記錄保持同步。3、當緩存中的持久化對象(位于緩存中的對象)之間存在循環關聯關系時,Sessioin會保證不出現訪問對象的死循環。
在Hibernate應用中Java對象的狀態:
臨時狀態(transient):剛用new語句創建,還沒有被持久化,不處于Session的緩存中。處于臨時狀態的Java對象稱為臨時對象。
持久化狀態(persistent):已經被持久化,加入到Session的緩存中。處于持久化狀態的Java對象稱為持久化對象。
游離狀態(detached):已經被持久化,但不再處于Session的緩存中。處于游離狀態的Java對象稱為游離對象。
Java對象:開始生命周期——》臨時狀態——》持久化狀態——》游離狀態——》結束生命周期
Session的保存、更新、刪除、查詢方法:
1、Session的save()方法
session.save(customer);完成的操作:
(1)把Customer對象加載到緩存中,使它變為持久化對象。
(2)選用映射文件指定的標識符生成器為持久化對象分配唯一的OID。Customer.hbm.xml文件中<id>元素的<generator>子元素指定標識符生成器。
(3)計劃執行一個insert語句,把Customer對象當前的屬性值組裝到insert語句中。
insert into CUSTOMERS(ID, NAME, ......) values(1, "Tom", ......)
save()方法并不立即執行SQL insert語句。只有當Session清理緩存時,才會執行SQL語句。
2、Session的update()方法
session.update(customer);完成的操作:
(1)把Customer對象重新加入到Session緩存中,使它變為持久化對象。
(2)計劃執行一個update語句。Session只有在清理緩存時才會執行update語句,并且在執行時才會把Customer對象當前的屬性值組裝到update語句中。
(3)只要通過update()方法使游離對象被一個Session關聯,即使沒有修改Customer對象的任何屬性,Session在清理緩存時也會執行由update()方法計劃的update語句。
update CUSTOMERS set NAME="Tom", ...... where ID=1
如果希望Session僅當修改了Customer對象的屬性時,才執行update語句,可把映射文件Customer.hbm.xml中<class>元素的"select-before-update"設為true(該屬性默認為false)
<class name="mypack.Customer" table="CUSTOMERS" select-before-update="true">
當Session清理緩存時,先執行一條select語句,然后比較Customer對象的屬性是否和從數據庫中檢索出來的記錄一致,只有在不一致時才執行update語句。
3、Session的saveOrUpdate()方法
如果傳入的參數是臨時對象,就調用save()方法;如果傳入的參數是游離對象,就調用update()方法;如果傳入的參數是持久化對象,那就直接返回。
Hibernate判斷臨時對象的條件:
(1)Java對象的OID取值為null
(2)Java對象具有version屬性并且取值為null
(3)在映射文件中為<id>元素設置了unsaved-value屬性,并且OID取值與unsaved-value屬性只匹配
(4)在映射文件中為version屬性設置了unsaved-value屬性,并且version屬性取值與unsaved-value屬性值匹配
(5)自定義了Hibernate的Interceptor實現類,并且Interceptor的isUnsaved()方法返回Boolean.TRUE
4、Session的load()方法和get()方法
根據給定的OID從數據庫中加載一個持久化對象。
當數據庫中不存在與OID對應的記錄時
load()方法,返回ObjectNotFoundException異常
get()方法,返回null。
5、Session的delete()方法
如果傳入的參數是持久化對象,Session就計劃執行一個delete語句;如果傳入的參數是游離對象,先使游離對象被Session關聯,使它變為持久化對象,然后計劃執行一個delete語句。
Session只有在清理緩存的時候才會執行delete語句。只有當調用Session的close()方法時,才會從Session的緩存中刪除該對象。
session.delete(customer);
session.delete("from Customer as c where c.id>8");
級聯操縱對象圖
在對象-關系關聯映射文件中,用于映射持久化類之間關聯關系的元素<set>、<many-to-one>、<one-to-one>有cascade屬性,用于指定如何操縱與當前對象關聯的其他對象。
cascade屬性可選值:none、save-update、delete、all、delete-orphan、all-delete-orphan
Hibernate與觸發器協同工作
能激發觸發器運行的事件:
插入(insert)記錄事件、更新(update)記錄事件、刪除(delete)記錄事件
利用攔截器(Interceptor)生成審計日志
用戶定義的攔截器必須實現org.hibernate.Interceptor接口。
Interceptor對象有兩種存放方式:
SessionFactory.openSession(Interceptor):為每個Session實例分配一個Interceptor實例,這個實例存放在Session范圍內。
Configuration.setInterceptor(Interceptor):為SessionFactory實例分配一個Interceptor實例,這個實例存放在SessionFactory范圍內,被所有Session實例共享。
操縱持久化對象
1. 理解Session的緩存:在Java里面,緩存通常是指Java對象的屬性占用的內存空間,通常是一些集合類型的屬性。在session接口的實現類SessionImpl中定義了一系列的Java集合,這些Java集合就構成了Session的緩存。當Session的save()方法持久化一個對象時,這個對象被加入到Session的緩存中,以后即使應用程序中的引用變量不再引用這個對象,只要Session的緩存還沒有被清空,這個對象仍然處于生命周期中。當Session的load()方法加載一個對象時,Session會先判斷緩存中是否已經存在這個對象了,如果存在,就不需要再到數據庫中重新加載了。Session的緩存有兩大作用:1.1. 減少訪問數據庫的頻率。1.2. 保證緩存中的對象與數據庫中的相關記錄保持同步。Session有兩個方法:一個commit()事務提交方法,還有flush()刷新緩存方法,都有著清理緩存的作用。flush()進行緩存的清理,執行一系列的SQL語句,但不會提交事務。而commit()方法會先調用flush()方法,然后在提交事務。2. 定義持久化類的建議:在應用程序中,用來實現業務問題實體的(如,在電子商務應用程序中的Customer和Order) 類就是持久化類。如果這些持久化類遵循一些簡單的規則,Hibernate能夠工作得更好,這些規則也被稱作簡單傳統Java對象(POJO:Plain Old Java Object)編程模型。但是這些規則并不是必需的。最好要遵循以下幾條主要的規則:1) 實現一個默認的(即無參數的)構造方法(constructor):我們強烈建議,在Hibernate中,為了運行期代理的生成,構造方法至少是包(package)內可見的。2) 提供一個標識屬性(identifier property):我們建議你對持久化類聲明命名一致的標識屬性。我們還建議你使用一個可以為空(也就是說,不是原始類型)的類型。3) 使用非final的類:代理(proxies)是Hibernate的一個重要的功能,它依賴的條件是,持久 化類或者是非final的,或者是實現了一個所有方法都聲明為public的接口。你也應該避免在非final類中聲明
public final
的方法。4) 為持久化字段聲明訪問器(accessors):5) 如果你有如下需求,你必須重載 equals() 和 hashCode()方法:l 想把持久類的實例放入Set中(當表示多值關聯時,推薦這么做)l 想重用脫管實例Hibernate保證,僅在特定會話范圍內,持久化標識(數據庫的行)和Java標識是等價的。因此,一旦 我們混合了從不同會話中獲取的實例,如果希望Set有明確的語義,就必須實現equals() 和hashCode()。3. 持久化對象的三種狀態:一個持久化類的實例可能處于三種不同狀態中的某一種。3.1. 瞬時(transient)狀態:該實例是剛用new語句創建的,還沒有被持久化,不處于任何Session的緩存中。它沒有持久化標識(相當于主鍵值)。處于瞬時狀態的實例被稱為瞬時對象。它的特點是:不和任何一個Session實例關聯。在數據庫中沒有對應的記錄。3.2. 持久化(persistent)狀態:已經被持久化,加入到Session緩存中。處于持久化狀態的實例被稱為持久化對象。實例目前與某個Session有關聯。 它擁有持久化標識(相當于主鍵值),并且可能在數據庫中有一個對應的行。 Hibernate保證在同一個Sesion實例的緩存中,數據庫中的每條記錄只對應唯一的持久化對象。 它的特點是: 持久化對象總是被一個Session實例關聯。持久化對象和數據庫中的相關記錄對應。Session在清理緩存時,會根據持久化對象的屬性變化,來同步更新數據庫。3.3. 脫管(detached)狀態:已經被持久化過,但不再處于Session的緩存中。處于脫管狀態的實例被稱為脫管對象。實例曾經與某個持久化上下文發生過關聯,不過那個上下文被關閉了, 或者這個實例是被序列化(serialize)到另外的進程。 它擁有持久化標識,并且在數據庫中可能存在一個對應的行。 對于脫管狀態的實例,Hibernate不保證任何持久化標識和Java標識的關系。它的特點是:不再位于session的緩存中,即它不再和session關聯。它擁有持久化標識。 Query query = session.createQuery("from User as u where u.age > ? and u.name like ?");query.setInteger(0, 25);query.setString(1, "%a%");List list = query.list(); |
Query query = session.createQuery("from User as u where u.age > :minAge and u.name like likeName");query.setInteger("minAge", 25);query.setString("likeName", "%a%");List list = query.list(); |
......<hibernate-mapping> <classname="org.qiujy.demo.User"table="user"> <idname="id"column="id"type="java.lang.Integer"> <generatorclass="native"/> </id> <propertyname="name"column="name"type="java.lang.String"/> <propertyname="age"column="age"type="java.lang.Integer"/> </class><query name="queryUserByAgeAndName"> <![CDATA[ from User as u where u.age >:minAge and u.name like :likeName]]></query></hibernate-mapping> |
Query query = session.getNamedQuery("queryUserByAgeAndName");query.setInteger("minAge", 25);query.setString("likeName", "%a%");List list = query.list(); |
新聞熱點
疑難解答