Hibernate操作和保存方式


Session API

【Java Hibernate 之 CRUD 操作】http://www.codeceo.com/article/java-hibernate-crud.html
 
【Session的入門常用方法】
Query query = session.createQuery(hql):利用hql查詢語句查詢;
Criteria critera = session.createCriteria(Class clazz);
Transaction tx = session.beginTransaction();     //開始事務;tx.commit()提交事務;
session.close();//關閉Session,此后被session管理的持久化對象變為脫管狀態;
session.save(Object obj);    //添加
session.update(Object obj);     //更新
session.delete(Object obj);    //刪除
Object obj = session.get(Class clazz,Serialiazble id);    //根據主鍵查找記錄並返回;
Object obj = session.load(Class clazz,Serializable id);    //和get方法效果一樣,但是是懶加載,即在不使用他之前他不會返回對象;
 
 

游離對象

【臨時狀態transient、持久狀態persistent和游離狀態detached 】
 
【游離對象】是由持久化對象轉變過來的,因此可能在數據庫中還存在對應的記錄 。從持久化的數據轉化而來。比如session關閉或者從session里delete這個對象。
        原來同Session有關聯關系,但當下卻沒有關聯關系了,這樣的對象就是detached的對象。
        session的update()或者saveOrUpdate()方法,重新將該detached對象同相應的seesion建立關聯關系。
 
【臨時對象】在數據庫中沒有對應的記錄。 和hibernate session無關。事物提交也不能改變數據庫對應的數據

        Transient對象指的是新建的持久化類的實例,它還從未同Hibernate的任何Session有過關聯關系。

        persist()或者save()方法,將transient對象變成persistent對象。

 

(1)當通過get或load方法得到的po對象它們都處於persistent,但如果執行delete(po)時(但還沒執行事務),該 po狀態就處於detached, (表示和session脫離關聯),因delete而變成游離態可以通過save或saveOrUpdate()變成持久態
(2)當一個session執行close()或clear()、evict()之后,session緩存中的persistent的po對象也變成detached,此時持久對象會變成脫管對象,此時該對象雖然具有數據庫識別值,但它已不在HIbernate持久層的管理之下。

update()

 

saveorupdate()如果傳入的對象在數據庫中有就做update操作,如果沒有就做save操作。

save()在數據庫中生成一條記錄,如果數據庫中有,會報錯說有重復的記錄。

update()就是更新數據庫中的記錄 主鍵在saveorupdate()方法中是起着關鍵作用的,只有這個主鍵的值不為空的時候才進行insert還是update的判斷,否則直接insert若主鍵不為空,就可以進行saveorupdate()操作了。 saveOrUpdate是當你一個pojo對象在不確定的情況下使用的,目的在於當這個對象存在的時候就將其狀態改變成現在這個狀態,如果不存在就使其持久化保存現在這個狀態,也就是說不論怎樣就是要有這個對象 save是返回插入數據的主見的,而saveOrUpdate是void

save方法更適用於確定了是要插入,而且需要得到插入數據的主鍵

而saveOrUpdate更傾向於不缺定是插入還是更新,而且你不需要得到他的主鍵

另一方面,如果你無法確定你要插入或更新的對象是不是持久態或游離態時。如果你save一個持久態或更新一個游離態,這都是有問題的,此時你就要用到saveOrUpdate

總體來說,如果你能確定你即將操作對象的狀態,則不需要用saveOrUpdate

 

 

【update()和flush區別?】

Hibernate update操作的是在自由態或脫管狀態(因session的關閉而處於脫管狀態)的對象,而flush是操作的在持久狀態的對象。
默認情況下,一個持久狀態的對象的改動(包含set容器)是不需要update的,只要你更改了對象的值,等待Hibernate flush就自動更新或保存到數據庫了。
(1) 調用某些查詢的和手動flush(),session的關閉、SessionFactory關閉結合。get()一個對象,把對象的屬性進行改變,把資源關閉。
(2)transaction commit的時候(包含了flush) 。

 

【update()和saveOrUpdate()的區別?】

(1)update() 更新,沒有主鍵會報錯的,saveOrUpdate() 保存或更新, 沒有主鍵就執行插入。
(2)Update:是對暫態(transient )或是只是脫管(detached)的更新操作,對於暫態對象的更新操作通常不產生效果,對於脫管對象是做了同步的操作,即數據庫的數據發生變化並且對象狀態也成為托管對象
SaveOrUpdate : 也是對暫態(transient )或是只是脫管(detached)的進行操作,至於是插入還是更新,則要根據(identifier)id 中指定的一些具體條件來分析,如果對象沒有持久化標識(identifier)屬性,對其調用save() ,否則update() 這個對象 。
(3)如果該po對象已經在本session中持久化了,在本session中執行saveOrUpdate(po)不做任何事
如果savaOrUpdate(給定id的新po)與另一個與本session關聯的po對象擁有相同的持久化標識(identifier),拋出一個NonUniqueObjectException異常 :a different object with the same identifier value was already associated with the session。

 

【update()和merge()區別?】

前面說過update,基本merge和update一樣。但如果session中存在相同持久化標識(identifier)的實例,用用戶給出的對象覆蓋session已有的持久實例
(1)當我們使用update的時候,執行完成后,會拋出異常
(2)但當我們使用merge的時候,把處理自由態的po對象A的屬性copy到session當中處於持久態的po的屬性中,執行完成后原來是持久狀態還是持久態,而我們提供的A還是自由態。

load()和get()的區別

Object Session.load(Class arg0, Serializable arg1) throws HibernateException

    * arg0:需要加載對象的類,例如:User.class

    * arg1:查詢條件(實現了序列化接口的對象):例"4028818a245fdd0301245fdd06380001"字符串已經實現了序列化接口。

    * 此方法返回類型為Object,但返回的是代理對象。

    * 執行此方法時不會立即發出查詢SQL語句。只有在使用對象時,它才發出查詢SQL語句,加載對象。

    * 因為load方法實現了lazy(稱為延遲加載、賴加載)

    * 延遲加載:只有真正使用這個對象的時候,才加載(才發出SQL語句)

    * hibernate延遲加載實現原理是代理方式。

    * 采用load()方法加載數據,如果數據庫中沒有相應的記錄,則會拋出異常對象不找到(org.hibernate.ObjectNotFoundException)

 

Hibernate兩種加載數據方式的區別:

    get()方法默認不支持lazy(延遲加載)功能,而load支持延遲加載

    get()方法在查詢不到數據時,返回null,而load因為支持延遲加載,只有在使用對象時才加載,所以如果數據庫中不在數據load會拋出異常(org.hibernate.ObjectNotFoundException)。

 

get()和load()只根據主鍵查詢,不能根據其它字段查詢,如果想根據非主鍵查詢,可以使用HQL

 


如果在緩存中沒有找到相應的對象,get將會直接訪問數據庫並返回一個完全初始化好的對象,而這個過程有可能會涉及到多個數據庫調用;

 

而load方法在緩存中沒有發現對象的情況下,只會返回一個代理對象,只有在對象getId()之外的其它方法被調用時才會真正去訪問數據庫,這樣就能在某些情況下大幅度提高性能。

 

 


Session.load/get方法均可以根據指定的實體類和id從數據庫讀取記錄,並返回與之對應的實體對象。其區別在於:

如果未能發現符合條件的記錄,get方法返回null,而load方法會拋出一個ObjectNotFoundException。


Load方法可返回實體的代理類實例,而get方法永遠直接返回實體類。


load方法可以充分利用內部緩存和二級緩存中的現有數據,而get方法則僅僅在內部緩存中進行數據查找,如沒有發現對應數據,將越過二級緩存,直接調用SQL完成數據讀取。

 

Session在加載實體對象時,將經過的過程:

首先,Hibernate中維持了兩級緩存。

 

第一級緩存由Session實例維護,其中保持了Session當前所有關聯實體的數據,也稱為內部緩存。

 

而第二級緩存則存在於SessionFactory層次,由當前所有由本 SessionFactory構造的Session實例共享。

 

出於性能考慮,避免無謂的數據庫訪問,Session在調用數據庫查詢功能之前,會先在緩存中進行查詢。首先在第一級緩存中,通過實體類型和id進行查找,如果第一級緩存查找命中,且數據狀態合法,則直接返回。
之后,Session會在當前“NonExists”記錄中進行查找,如果“NonExists”記錄中存在同樣的查詢條件,則返回null。 “NonExists”記錄了當前Session實例在之前所有查詢操作中,未能查詢到有效數據的查詢條件(相當於一個查詢黑名單列表)。如此一來,如果 Session中一個無效的查詢條件重復出現,即可迅速作出判斷,從而獲得最佳的性能表現。

對於load方法而言,如果內部緩存中未發現有效數據,則查詢第二級緩存,如果第二級緩存命中,則返回。


如在緩存中未發現有效數據,則發起數據庫查詢操作(Select SQL),如經過查詢未發現對應記錄,則將此次查詢的信息在“NonExists”中加以記錄,並返回null。
根據映射配置和Select SQL得到的ResultSet,創建對應的數據對象。
將其數據對象納入當前Session實體管理容器(一級緩存)。
執行Interceptor.onLoad方法(如果有對應的Interceptor)。
將數據對象納入二級緩存。
如果數據對象實現了LifeCycle接口,則調用數據對象的onLoad方法。
返回數據對象。

save、persist和saveOrUpdate這三個方法的不同之處?

都是用於將對象保存到數據庫中的方法。

 

save()只能INSERT記錄

saveOrUpdate()可以進行 記錄的INSERT和UPDATE。

save()的返回值是一個Serializable對象

persist()方法返回值為void。

 

getCurrentSession()與openSession()

都通過Hibernate的SessionFactory獲得:

1.getCurrentSession創建的session會和綁定到當前線程,而openSession不會。

2.getCurrentSession創建的線程會在事務回滾或事物提交后自動關閉,而openSession必須手動關閉。



對於getCurrentSession()方法: 
       (1)其所在方法必須進行事務控制 
       (2)Session在第一次被使用的時候,或者第一次調用getCurrentSession()的時候,其生命周期就開始。然后它被Hibernate綁定到當前線程。當事務結束的時候,不管是提交還是回滾,Hibernate也會把Session從當前線程剝離,並且關閉它。假若你再次調用getCurrentSession(),你會得到一個新的Session,並且開始一個新的工作單元。     
   
對於openSession()方法: 
        這個方法一般在spring與Hibernate的集成中不直接使用,它就是打開一個session,並且這個session與上下文無關,如果對其所在方法進行事務控制,會發現不起作用,原因就是前面提到的,事務控制必須確保是同一個連接,而openSession()打開的session與上下文無關。
 
這個方法與getSession(),getCurrentSession()以及getHibernateTemplate()等方法的區別在於:后面的幾個方法spring可以對其進行控制。
如果對它們所在的方法進行事務控制,spring可以確保是同一個連接,而openSession()方法,spring無法對其進行控制,所以事務也不會起作用。

lock()

        Session的lock()方法重建了關聯關系卻並沒有同數據庫進行同步和更新。因此,你在使用lock()方法時一定要多加小心。順便說一下,在進行關聯關系重建時,你可以隨時使用Session的update()方法同數據庫進行同步。有時這個問題也可以這么來問:Session的lock()方法和update()方法之間有什么區別?。

 


【lock()方法與update()方法的區別】
在執行lock()方法時,會立即使用查詢語句查詢版本號進行版本檢查,並不會執行一個update操作。 
 
而update()方法,並不會進行版本檢查,直到session.flush()時候,會先進行版本檢查,再進行更新操作。
 
這兩個方法都把一個游離對象與一個session實例關聯起來。
 
 
lock是把一個沒有更改過的脫管狀態的對象變成持久狀態
1.調用lock把對象從脫管狀態變成持久狀態
2.更改持久狀態的對象的內容
3.等待flush或者手動flush 
 

 
lock和update區別
update是把一個已經更改過的脫管狀態的對象變成持久狀態
lock是把一個沒有更改過的脫管狀態的對象變成持久狀態(針對的是因Session的關閉而處於脫管狀態的po對象(2),不能針對因delete而處於脫管狀態的po對象)
對應更改一個記錄的內容,兩個的操作不同:
update的操作步驟是:
(1)屬性改動后的脫管的對象的修改->調用update
lock的操作步驟是:
(2)調用lock把未修改的對象從脫管狀態變成持久狀態-->更改持久狀態的對象的內容-->等待flush或者手動flush

SessionFactory

SessionFactory有什么作用? SessionFactory是線程安全的嗎?

    這也是Hibernate框架的常見面試問題。顧名思義,SessionFactory就是一個用於創建Hibernate的Session對象的工廠。SessionFactory通常是在應用啟動時創建好的,應用程序中的代碼用它來獲得Session對象。作為一個單個的數據存儲,它也是 線程安全的,所以多個線程可同時使用同一個SessionFactory。Java JEE應用一般只有一個SessionFactory,服務於客戶請求的各線程都通過這個工廠來獲得Hibernate的Session實例,這也是為什么SessionFactory接口的實現必須是線程安全的原因。還有,SessionFactory的內部狀態包含着同對象關系影射有關的所有元數據,它是不可變的,一旦創建好后就不能對其進行修改了。

 

hibernate的各種保存方式的區別(save,persist,update,saveOrUpdte,merge,flush,lock)及對象的三種狀態


hibernate的保存


hibernate對於對象的保存提供了太多的方法,他們之間有很多不同,這里細說一下,以便區別。
一、預備知識
對於hibernate,它的對象有三種狀態,transient、persistent、detached
下邊是常見的翻譯辦法:
transient:瞬態或者自由態
(new DeptPo(1,”行政部”,20,”行政相關”),該po的實例和session沒有關聯,該po的實例處於transient)
persistent:持久化狀態
(和數據庫中記錄想影射的Po實例,它的狀態是persistent, 通過get和load等得到的對象都是persistent)
detached:脫管狀態或者游離態
(1)當通過get或load方法得到的po對象它們都處於persistent,但如果執行delete(po)時(但不能執行事務),該po狀態就處於detached, (表示和session脫離關聯),因delete而變成游離態可以通過save或saveOrUpdate()變成持久態
(2)當把session關閉時,session緩存中的persistent的po對象也變成detached
因關閉session而變成游離態的可以通過lock、save、update變成持久態
持久態實例可以通過調用 delete()變成脫管狀態。
通過get()或load()方法得到的實例都是持久化狀態的。
脫管狀態的實例可以通過調用lock()或者replicate()進行持久化。

save()和persist()將會引發SQL的INSERT,delete()會引發SQLDELETE,
而update()或merge()會引發SQL UPDATE。對持久化(persistent)實例的修改在刷新提交的時候會被檢測到,它也會引起SQL UPDATE。
saveOrUpdate()或者replicate()會引發SQLINSERT或者UPDATE


二、save 和update區別
把這一對放在第一位的原因是因為這一對是最常用的。
save的作用是把一個新的對象保存
update是把一個脫管狀態的對象或自由態對象(一定要和一個記錄對應)更新到數據庫

 

三、update 和saveOrUpdate區別
這個是比較好理解的,顧名思義,saveOrUpdate基本上就是合成了save和update,而update只是update;引用hibernate reference中的一段話來解釋他們的使用場合和區別
通常下面的場景會使用update()或saveOrUpdate(): 
程序在第一個session中加載對象,接着把session關閉 
該對象被傳遞到表現層 
對象發生了一些改動 
該對象被返回到業務邏輯層最終到持久層 
程序創建第二session調用第二個session的update()方法持久這些改動

saveOrUpdate(po)做下面的事: 
如果該po對象已經在本session中持久化了,在本session中執行saveOrUpdate不做任何事 
如果savaOrUpdate(新po)與另一個與本session關聯的po對象擁有相同的持久化標識(identifier),拋出一個異常 
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.itfuture.www.po.Xtyhb#5]
saveOrUpdate如果對象沒有持久化標識(identifier)屬性,對其調用save() ,否則update() 這個對象

 

四、persist和save區別
這個是最迷離的一對,表面上看起來使用哪個都行,在hibernate reference文檔中也沒有明確的區分他們.
這里給出一個明確的區分。(可以跟進src看一下,雖然實現步驟類似,但是還是有細微的差別)
主要內容區別:
1,persist把一個瞬態的實例持久化,但是並"不保證"標識符(identifier主鍵對應的屬性)被立刻填入到持久化實例中,標識符的填入可能被推遲到flush的時候。

2,save, 把一個瞬態的實例持久化標識符,及時的產生,它要返回標識符,所以它會立即執行Sql insert

 

五、saveOrUpdate,merge和update區別
比較update和merge
update的作用上邊說了,這里說一下merge的
如果session中存在相同持久化標識(identifier)的實例,用用戶給出的對象覆蓋session已有的持久實例 
(1)當我們使用update的時候,執行完成后,會拋出異常 
(2)但當我們使用merge的時候,把處理自由態的po對象A的屬性copy到session當中處於持久態的po的屬性中,執行完成后原來是持久狀態還是持久態,而我們提供的A還是自由態

 

六、flush和update區別
這兩個的區別好理解
update操作的是在自由態或脫管狀態(因session的關閉而處於脫管狀態)的對象//updateSQL
而flush是操作的在持久狀態的對象。
默認情況下,一個持久狀態的對象的改動(包含set容器)是不需要update的,只要你更改了對象的值,等待hibernate flush就自動更新或保存到數據庫了。hibernate flush發生在以下幾種情況中:
1, 調用某些查詢的和手動flush(),session的關閉、SessionFactory關閉結合 
get()一個對象,把對象的屬性進行改變,把資源關閉。
2,transaction commit的時候(包含了flush)

 

 


八、clear和evcit的區別
clear完整的清除session緩存
evcit(obj)把某個持久化對象從session的緩存中清空。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM