Session API
游離對象
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()之外的其它方法被調用時才會真正去訪問數據庫,這樣就能在某些情況下大幅度提高性能。
如果未能發現符合條件的記錄,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與上下文無關。
lock()
Session的lock()方法重建了關聯關系卻並沒有同數據庫進行同步和更新。因此,你在使用lock()方法時一定要多加小心。順便說一下,在進行關聯關系重建時,你可以隨時使用Session的update()方法同數據庫進行同步。有時這個問題也可以這么來問:Session的lock()方法和update()方法之間有什么區別?。
update是把一個已經更改過的脫管狀態的對象變成持久狀態
lock是把一個沒有更改過的脫管狀態的對象變成持久狀態(針對的是因Session的關閉而處於脫管狀態的po對象(2),不能針對因delete而處於脫管狀態的po對象)
對應更改一個記錄的內容,兩個的操作不同:
update的操作步驟是:
(1)屬性改動后的脫管的對象的修改->調用update
lock的操作步驟是:
(2)調用lock把未修改的對象從脫管狀態變成持久狀態-->更改持久狀態的對象的內容-->等待flush或者手動flush
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的緩存中清空。