1、簡述一下hibernate的開發(fā)流程?
● 第一步:加載hibernate的配置文件,讀取配置文件的參數(shù)(jdbc連接參數(shù),數(shù)據(jù)庫方言,hbm表與對象關(guān)系映射文件)
● 第二步:創(chuàng)建SessionFactory會話工廠(內(nèi)部有連接池)
● 第三步:打開session獲取連接,構(gòu)造session對象(一次會話維持一個(gè)數(shù)據(jù)連接,也是一級緩存)
● 第四步:開啟事務(wù)
● 第五步:進(jìn)行操作
● 第六步:提交事務(wù)
● 第七步:關(guān)閉session(會話)將連接釋放
● 第八步:關(guān)閉連接池
瞬時(shí)態(tài)(臨時(shí)態(tài)、自由態(tài)):不存在持久化標(biāo)識OID,尚未與Hibernate Session關(guān)聯(lián)對象,被認(rèn)為處于瞬時(shí)態(tài),失去引用將被JVM回收。
持久態(tài):存在持久化標(biāo)識OID,與當(dāng)前session有關(guān)聯(lián),并且相關(guān)聯(lián)的session沒有關(guān)閉,并且事務(wù)未提交。
脫管態(tài)(離線態(tài)、游離態(tài)):存在持久化標(biāo)識OID,但沒有與當(dāng)前session關(guān)聯(lián),脫管狀態(tài)改變hibernate不能檢測到。
● Hibernate一級緩存(Session的緩存):
Session實(shí)現(xiàn)了第一級Cache,屬于事務(wù)級數(shù)據(jù)緩沖。一旦事務(wù)結(jié)束,緩存隨之失效。一個(gè)Session的生命周期對應(yīng)一個(gè)數(shù)據(jù)庫事務(wù)或一個(gè)程序事務(wù)。
Session-Cache總是被打開并且不能被關(guān)閉的。
Session-Cache保證一個(gè)Session中兩次請求同一個(gè)對象時(shí),取得的對象是同一個(gè)Java實(shí)例,有時(shí)它可以避免不必要的數(shù)據(jù)沖突。
在對于同一個(gè)對象進(jìn)行循環(huán)引用時(shí),不至于產(chǎn)生堆棧溢出。
當(dāng)數(shù)據(jù)庫事務(wù)結(jié)束時(shí),對于同一數(shù)據(jù)表行,不會產(chǎn)生數(shù)據(jù)沖突。因?yàn)閷τ跀?shù)據(jù)庫中的一行,最多有一個(gè)對象來表示它。
一個(gè)事務(wù)中可能會有很多個(gè)處理單元,在每一個(gè)處理單元中做的操作都會立即被其他的數(shù)據(jù)單元得知。
● 二級緩存是SessionFactory范圍內(nèi)的緩存,所有的Session共享同一個(gè)二級緩存。在二級緩存中保存持久化實(shí)例的散裝形式的數(shù)據(jù)。
● 持久化不同的數(shù)據(jù)需要不同的Cache策略,比如一些因素將影響Cache策略的選擇:數(shù)據(jù)的讀/寫比例、數(shù)據(jù)表是否能被其他的應(yīng)用程序所訪問等。
● 設(shè)置Hibernate二級緩存需要分兩步:首先,確認(rèn)使用什么數(shù)據(jù)并發(fā)策略。然后,配置緩存過期時(shí)間并設(shè)置Cache提供器。
Hibernate的查詢方式常見的主要分為三種:HQL,QBC(命名查詢),以及使用原生SQL查詢(SqlQuery)
● 兩者相同點(diǎn):
Hibernate與MyBatis都可以是通過SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory生成Session,最后由Session來開啟執(zhí)行事務(wù)和SQL語句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。
Hibernate和MyBatis都支持JDBC和JTA事務(wù)處理。
● Mybatis優(yōu)勢:
MyBatis可以進(jìn)行更為細(xì)致的SQL優(yōu)化,可以減少查詢字段。
MyBatis容易掌握,而Hibernate門檻較高。
● Hibernate優(yōu)勢:
Hibernate的DAO層開發(fā)比MyBatis簡單,Mybatis需要維護(hù)SQL和結(jié)果映射。
Hibernate對對象的維護(hù)和緩存要比MyBatis好,對增刪改查的對象維護(hù)要方便。
Hibernate數(shù)據(jù)庫移植性很好,MyBatis的數(shù)據(jù)庫移植性不好,不同的數(shù)據(jù)庫需要寫不同SQL。
Hibernate有更好的二級緩存機(jī)制,可以使用第三方緩存。MyBatis本身提供的緩存機(jī)制不佳。
● 相同點(diǎn):
兩者都是java數(shù)據(jù)庫操作的中間件。
兩者對數(shù)據(jù)庫進(jìn)行直接操作的對象都是線程不安全的,都需要及時(shí)關(guān)閉。
兩者都可對數(shù)據(jù)庫的更新操作進(jìn)行顯式的事務(wù)處理。
● 不同點(diǎn):
JDBC是SUN公司提供一套操作數(shù)據(jù)庫的規(guī)范,使用java代碼操作數(shù)據(jù)庫。Hibernate是一個(gè)基于jdbc的主流持久化框架,對JDBC訪問數(shù)據(jù)庫的代碼做了封裝。
使用的SQL語言不同:JDBC使用的是基于關(guān)系型數(shù)據(jù)庫的標(biāo)準(zhǔn)SQL語言,Hibernate使用的是HQL(Hibernate query language)語言。
操作的對象不同:JDBC操作的是數(shù)據(jù),將數(shù)據(jù)通過SQL語句直接傳送到數(shù)據(jù)庫中執(zhí)行,Hibernate操作的是持久化對象,由底層持久化對象的數(shù)據(jù)更新到數(shù)據(jù)庫中。
數(shù)據(jù)狀態(tài)不同:JDBC操作的數(shù)據(jù)是“瞬時(shí)”的,變量的值無法與數(shù)據(jù)庫中的值保持一致,而Hibernate操作的數(shù)據(jù)是可持久的,即持久化對象的數(shù)據(jù)屬性的值是可以跟數(shù)據(jù)庫中的值保持一致的。
ORM指的是對象關(guān)系型映射(Object RelationShip Mapping),指的就是我們通過創(chuàng)建實(shí)體類對象和數(shù)據(jù)庫中的表關(guān)系進(jìn)行一一對應(yīng),來實(shí)現(xiàn)通過操作實(shí)體類對象來更改數(shù)據(jù)庫里邊的數(shù)據(jù)信息。這里邊起到關(guān)鍵作用的是通過Hibernate的映射文件+Hibernate的核心配置文件。
● get是立即加載,load是延時(shí)加載。
● get會先查一級緩存,再查二級緩存,然后查數(shù)據(jù)庫;load會先查一級緩存,如果沒有找到就創(chuàng)建代理對象,等需要的時(shí)候去查詢二級緩存和數(shù)據(jù)庫。(這里就體現(xiàn)load的延遲加載的特性。)
● get如果沒有找到會返回null,load如果沒有找到會拋出異常。
● 當(dāng)我們使用session.load()方法來加載一個(gè)對象時(shí),此時(shí)并不會發(fā)出sql語句,當(dāng)前得到的這個(gè)對象其實(shí)是一個(gè)代理對象,這個(gè)代理對象只保存了實(shí)體對象的id值,只有當(dāng)我們要使用這個(gè)對象,得到其它屬性時(shí),這個(gè)時(shí)候才會發(fā)出sql語句,從數(shù)據(jù)庫中去查詢我們的對象;相對于load的延遲加載方式,get就直接的多,當(dāng)我們使用session.get()方法來得到一個(gè)對象時(shí),不管我們使不使用這個(gè)對象,此時(shí)都會發(fā)出sql語句去從數(shù)據(jù)庫中查詢出來。
● 數(shù)據(jù)庫設(shè)計(jì)調(diào)整。
● HQL優(yōu)化。
● API的正確使用(如根據(jù)不同的業(yè)務(wù)類型選用不同的集合及查詢API)。
● 主配置參數(shù)(日志,查詢緩存,fetch_size,batch_size等)。
● 映射文件優(yōu)化(ID生成策略,二級緩存,延遲加載,關(guān)聯(lián)優(yōu)化)。
● 一級緩存的管理。
● 針對二級緩存,還有許多特有的策略。
● 事務(wù)控制策略。
延遲加載機(jī)制是為了避免一些無謂的性能開銷而提出來的,所謂延遲加載就是當(dāng)在真正需要數(shù)據(jù)的時(shí)候,才真正執(zhí)行數(shù)據(jù)加載操作。在Hibernate中提供了對實(shí)體對象的延遲加載以及對集合的延遲加載,另外在Hibernate3中還提供了對屬性的延遲加載。
延遲加載的過程:通過代理(Proxy)機(jī)制來實(shí)現(xiàn)延遲加載。Hibernate從數(shù)據(jù)庫獲取某一個(gè)對象數(shù)據(jù)時(shí)、獲取某一個(gè)對象的集合屬性值時(shí),或獲取某一個(gè)對象所關(guān)聯(lián)的另一個(gè)對象時(shí),由于沒有使用該對象的數(shù)據(jù)(除標(biāo)識符外),Hibernate并不從數(shù)據(jù)庫加載真正的數(shù)據(jù),而只是為該對象創(chuàng)建一個(gè)代理對象來代表這個(gè)對象,這個(gè)對象上的所有屬性都為默認(rèn)值;只有在真正需要使用該對象的數(shù)據(jù)時(shí)才創(chuàng)建這個(gè)真正的對象,真正從數(shù)據(jù)庫中加載它的數(shù)據(jù)。
No session問題報(bào)錯(cuò)如下:
根據(jù)字面上的意思,是指代理不能被初始化,session已經(jīng)關(guān)閉。
● No session問題產(chǎn)生的原因:
當(dāng)執(zhí)行Session的load()方法時(shí),Hibernate不會立即執(zhí)行查詢所查詢對象關(guān)聯(lián)的對象(在此我們統(tǒng)稱被關(guān)聯(lián)的對象類為A類),僅僅返回A類的代理類的實(shí)例,這個(gè)代理類具由以下特征:
由Hibernate在運(yùn)行時(shí)動(dòng)態(tài)生成,它擴(kuò)展了A類,因此它繼承了A類的所有屬性和方法,但它的實(shí)現(xiàn)對于應(yīng)用程序是透明的。
當(dāng)Hibernate創(chuàng)建A類代理類實(shí)例時(shí),僅僅初始化了它的OID屬性,其他屬性都為null,因此這個(gè)代理類實(shí)例占用的內(nèi)存很少。
當(dāng)應(yīng)用程序第一次訪問A代理類實(shí)例時(shí)(例如調(diào)用a..getXXX()或a.setXXX()方法),Hibernate會初始化代理類實(shí)例,在初始化過程中執(zhí)行select語句,真正從數(shù)據(jù)庫中加載A對象的所有數(shù)據(jù)。但有個(gè)例外,那就是當(dāng)應(yīng)用程序訪問A代理類實(shí)例的getId()方法時(shí),Hibernate不會初始化代理類實(shí)例,因?yàn)樵趧?chuàng)建代理類實(shí)例時(shí)OID就存在了,不必到數(shù)據(jù)庫中去查詢。
Hibernate采用CGLIB工具來生成持久化類的代理類。CGLIB是一個(gè)功能強(qiáng)大的Java字節(jié)碼生成工具,它能夠在程序運(yùn)行時(shí)動(dòng)態(tài)生成擴(kuò)展Java類或者實(shí)現(xiàn)Java接口的代理類。
Hibernate中如果采用load加載的話(默認(rèn)的是延遲加載),也就是lazy=true操作,因此,當(dāng)調(diào)用完load后,session即可關(guān)閉。又因?yàn)槲覀兊膕ession只是放置到了Dao層,表現(xiàn)層根本獲取不到,所以在表現(xiàn)層調(diào)用的時(shí)候,session已經(jīng)關(guān)閉,報(bào)錯(cuò)。
Java動(dòng)態(tài)代理是利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。而cglib動(dòng)態(tài)代理是利用asm開源包,對代理對象類的class文件加載進(jìn)來,通過修改其字節(jié)碼生成子類來處理。
● 如果目標(biāo)對象實(shí)現(xiàn)了接口,默認(rèn)情況下會采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP,或者強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP。
● 如果目標(biāo)對象沒有實(shí)現(xiàn)了接口,必須采用CGLIB庫,Spring會自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB之間轉(zhuǎn)換。
● 減少訪問數(shù)據(jù)庫的頻率。應(yīng)用程序從內(nèi)存中讀取持久化對象的速度顯然比到數(shù)據(jù)庫中查詢數(shù)據(jù)的速度快多了,因此Session的緩存可以提高數(shù)據(jù)訪問的性能。
● 保證緩存中的對象與數(shù)據(jù)庫中的相關(guān)記錄保持同步。當(dāng)緩存中持久化對象的狀態(tài)發(fā)生了變換,Session并不會立即執(zhí)行相關(guān)的SQL語句,這使得Session能夠把幾條相關(guān)的SQL語句合并為一條SQL語句,以便減少訪問數(shù)據(jù)庫的次數(shù),從而提高應(yīng)用程序的性能。
Session清理緩存是指按照緩存中對象的狀態(tài)的變化來同步更新數(shù)據(jù)庫;清空是Session的關(guān)閉;
● 不是線程安全的,因此在設(shè)計(jì)軟件架構(gòu)時(shí),應(yīng)該避免多個(gè)線程共享同一個(gè)Session實(shí)例。
● Session實(shí)例是輕量級的,所謂輕量級是指它的創(chuàng)建和銷毀不需要消耗太多的資源。這意味著在程序中可以經(jīng)常創(chuàng)建或銷毀Session對象,例如為每個(gè)客戶請求分配單獨(dú)的Session實(shí)例,或者為每個(gè)工作單元分配單獨(dú)的Session實(shí)例。
● 在Session中,每個(gè)數(shù)據(jù)庫操作都是在一個(gè)事務(wù)(transaction)中進(jìn)行的,這樣就可以隔離開不同的操作(甚至包括只讀操作)。
● 立即檢索
● 優(yōu)點(diǎn):對應(yīng)用程序完全透明,不管對象處于持久化狀態(tài),還是游離狀態(tài),應(yīng)用程序都可以方便的從一個(gè)對象導(dǎo)航到與它關(guān)聯(lián)的對象;
● 缺點(diǎn):1.select語句太多;2.可能會加載應(yīng)用程序不需要訪問的對象白白浪費(fèi)許多內(nèi)存空間;
● 延遲檢索
● 優(yōu)點(diǎn):由應(yīng)用程序決定需要加載哪些對象,可以避免可執(zhí)行多余的select語句,以及避免加載應(yīng)用程序不需要訪問的對象。因此能提高檢索性能,并且能節(jié)省內(nèi)存空間;
● 缺點(diǎn):應(yīng)用程序如果希望訪問游離狀態(tài)代理類實(shí)例,必須保證他在持久化狀態(tài)時(shí)已經(jīng)被初始化;
● 迫切左外連接檢索
●優(yōu)點(diǎn):
1、對應(yīng)用程序完全透明,不管對象處于持久化狀態(tài),還是游離狀態(tài),應(yīng)用程序都可以方便地沖一個(gè)對象導(dǎo)航到與它關(guān)聯(lián)的對象。
2、使用了外連接,select語句數(shù)目少;
● 缺點(diǎn):
1、可能會加載應(yīng)用程序不需要訪問的對象,白白浪費(fèi)許多內(nèi)存空間;
2、復(fù)雜的數(shù)據(jù)庫表連接也會影響檢索性能;???????