小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

如何高效使用JavaEE ORM框架

 集微筆記 2013-10-25

  雖然Java領(lǐng)域有無(wú)數(shù)的ORM框架,如Hibernate,iBatis,TopLink,JDO,JPA……但是這些ORM框架基本上大同小異。很多初學(xué)者對(duì)JDBC的復(fù)雜性望而卻步,就簡(jiǎn)單認(rèn)為使用ORM就會(huì)省時(shí)省力,結(jié)果恰恰相反,任何好的框架都是給專家準(zhǔn)備的,任何急功近利試圖偷懶的方法往往適得其反。要正確使用ORM還真不是一件簡(jiǎn)單的事情。本文僅簡(jiǎn)單整理一下ORM的原理,基本用法,以及如何避免各種陷阱的基本編程原則。

  ORM的原理

  先說(shuō)ORM的實(shí)現(xiàn)原理。其實(shí),要實(shí)現(xiàn)JavaBean的屬性到數(shù)據(jù)庫(kù)表的字段的映射,任何ORM框架不外乎是讀某個(gè)配置文件把JavaBean的屬性和數(shù)據(jù)庫(kù)表的字段自動(dòng)關(guān)聯(lián)起來(lái),當(dāng)從數(shù)據(jù)庫(kù)Query時(shí),自動(dòng)把字段的值塞進(jìn)JavaBean的對(duì)應(yīng)屬性里,當(dāng)做INSERT或UPDATE時(shí),自動(dòng)把JavaBean的屬性值綁定到SQL語(yǔ)句中。但是,幾乎所有的ORM都提供“按需讀取”的功能,比如一個(gè)User有id,name,email和address這4個(gè)字段,但是address字段很少用,于是ORM只讀取前3個(gè)字段,直到調(diào)用User的getAddress()方法時(shí),才去數(shù)據(jù)庫(kù)中讀取address的值。這個(gè)功能顯然不能通過(guò)User的get/set完成,因此,ORM需要采用某種方式生成一個(gè)User類的子類,并且覆寫get/set方法,這樣,才能在調(diào)用get方法時(shí)有機(jī)會(huì)從數(shù)據(jù)庫(kù)中讀取。類似的對(duì)User的修改檢測(cè)也是這樣實(shí)現(xiàn)的。

  兩種增強(qiáng)的方式

  ORM為我們自己的JavaBean實(shí)現(xiàn)子類的方法很多,這個(gè)過(guò)程簡(jiǎn)單稱之為“增強(qiáng)”,基本上有兩種方法:Hibernate使用CGLIB在加載我們的User類時(shí)動(dòng)態(tài)創(chuàng)建了一個(gè)子類,而JDO則要求編譯完User類后再利用它提供的工具對(duì)User類進(jìn)行改造,以便實(shí)現(xiàn)JDO需要的各種接口。請(qǐng)注意:就是這種極其變態(tài)的設(shè)計(jì)導(dǎo)致了使用JDO的極大困難,在我們編譯完源碼后,還需要額外執(zhí)行一個(gè)增強(qiáng)命令,或者額外編寫Ant任務(wù),極大地影響了快速開發(fā)和單元測(cè)試,所以,凡是采用靜態(tài)生成持久類的ORM,要在第一時(shí)間直接排除,切記!

  理解持久和非持久狀態(tài)

  所有的ORM框架都有持久和非持久的概念。簡(jiǎn)單地說(shuō),當(dāng)我們new一個(gè)User實(shí)例時(shí),它是非持久對(duì)象,當(dāng)我們調(diào)用ORM的save()之類的方法后,這個(gè)實(shí)例就變成持久對(duì)象了。當(dāng)我們通過(guò)ORM從數(shù)據(jù)庫(kù)讀取到一個(gè)User對(duì)象時(shí),這個(gè)對(duì)象是持久對(duì)象,當(dāng)關(guān)閉當(dāng)前的事務(wù)后,這個(gè)對(duì)象變成非持久對(duì)象。

  雖然這個(gè)過(guò)程很容易理解,但是,難點(diǎn)在于當(dāng)我們?cè)O(shè)計(jì)一個(gè)方法時(shí),我們必須準(zhǔn)確地知道當(dāng)前操作的對(duì)象是持久對(duì)象還是非持久對(duì)象,否則,各種靈異事件會(huì)接踵而來(lái),比如插入了重復(fù)記錄等等。舉例說(shuō)明,當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)User對(duì)象時(shí),save(User)方法必須傳入非持久對(duì)象,當(dāng)我們需要更新一個(gè)User對(duì)象時(shí),update(User)方法必須傳入一個(gè)持久對(duì)象,有些ORM比如Hibernate,為了方便用戶,提供了saveOrUpdate()方法,自動(dòng)判斷是否是持久對(duì)象,是則更新,否則創(chuàng)建。我的建議是永遠(yuǎn)不要使用這些看上去很簡(jiǎn)單的方法,否則將很難判斷ORM到底做了什么操作,也就很難追蹤到邏輯錯(cuò)誤。

  正確使用CRUD

  正因?yàn)槲覀冃枰獣r(shí)刻區(qū)分一個(gè)對(duì)象的持久化狀態(tài),所以,編寫CRUD(Create,Retrieve,Update,Delete的縮寫)要嚴(yán)格遵循以下原則:

  Create:對(duì)于Create操作,傳入的永遠(yuǎn)是非持久化對(duì)象,一旦調(diào)用了create方法,就變成持久化對(duì)象;

  Retrieve:所有通過(guò)ORM從數(shù)據(jù)庫(kù)讀取的對(duì)象都是持久化對(duì)象,直到當(dāng)前會(huì)話結(jié)束;

  Update:對(duì)于Update操作,傳入的必須是持久化對(duì)象,而通常需要更新的對(duì)象是從頁(yè)面獲得的,因此,編寫Update方法要按照以下步驟:

  從Web頁(yè)面中獲得了一個(gè)User對(duì)象(包含ID),這個(gè)對(duì)象肯定是非持久化對(duì)象;

  當(dāng)?shù)玫皆揢ser對(duì)象時(shí),千萬(wàn)不可直接做Update操作,因?yàn)閺腤eb頁(yè)面得到的數(shù)據(jù)都是不可信任的,修改HTTP請(qǐng)求非常簡(jiǎn)單,有經(jīng)驗(yàn)的開發(fā)人員利用一個(gè)FireFox插件就能完成。正確的做法是根據(jù)該User對(duì)象的ID從數(shù)據(jù)庫(kù)中查詢到持久化的User對(duì)象,然后把待修改的屬性復(fù)制到持久化的User對(duì)象中,最后Update該持久化的User對(duì)象,簡(jiǎn)單的代碼如下:

  view sourceprint?01.void update(User u) {

  02. // 從數(shù)據(jù)庫(kù)讀取User:

  03. User p = load(User.class, u.getId());

  04. // 檢查當(dāng)前用戶有無(wú)權(quán)限修改:

  05. // TODO: ...

  06. // 復(fù)制屬性:

  07. p.setName(u.getName());

  08. p.setAddress(u.getAddress());

  09. // 不允許修改的屬性例如email就不要復(fù)制了,然后更新:

  10. update(p);

  11.}

  Delete:對(duì)于Delete操作,ORM通常只關(guān)心映射到主鍵的ID屬性,不過(guò),正確的做法仍然是根據(jù)ID先通過(guò)ORM讀取,然后判斷權(quán)限,最后刪除。簡(jiǎn)單的代碼如下:

  view sourceprint?1.void delete(String id) {

  2. // 從數(shù)據(jù)庫(kù)讀取User:

  3. User p = load(User.class, u.getId());

  4. // 檢查當(dāng)前用戶有無(wú)權(quán)限修改:

  5. // TODO: ...

  6. // 刪除:

  7. delete(p);

  8.}

  嚴(yán)格按照正確的方法做CRUD操作,使用ORM才能事半功倍。

  級(jí)聯(lián)讀取

  數(shù)據(jù)庫(kù)表支持外鍵關(guān)聯(lián),因此,ORM也可以把多個(gè)JavaBean按照數(shù)據(jù)庫(kù)的外鍵關(guān)系聯(lián)系起來(lái),比如可以在讀User對(duì)象時(shí)把其關(guān)聯(lián)的Profile對(duì)象也一并讀出來(lái),即所謂級(jí)聯(lián)讀取。這又是一個(gè)使用起來(lái)要非常小心謹(jǐn)慎的功能。

  首先,我的建議是級(jí)聯(lián)讀取的層次最好是0或1,一般不要超過(guò)3,千萬(wàn)不可設(shè)為無(wú)限,否則,一個(gè)簡(jiǎn)單的查詢可能就讀取了上萬(wàn)條記錄,在開發(fā)時(shí)由于并發(fā)用戶只有1,往往看不出問(wèn)題,等到部署了發(fā)現(xiàn)服務(wù)器經(jīng)常內(nèi)存溢出,其實(shí)是級(jí)聯(lián)讀取太多導(dǎo)致的。

  其次,級(jí)聯(lián)有一對(duì)多和多對(duì)一兩種(一對(duì)一可以并入第二種),要非常小心地使用一對(duì)多,除非有十分的把握確定“多”的一端只有不超過(guò)100條記錄。比如設(shè)計(jì)論壇時(shí)讀取“版面”Board時(shí)如果有一對(duì)多順便把“話題”Topic一并讀入了,隨著Topic越來(lái)越多,每次讀取Board的內(nèi)存占用也越來(lái)越多,直到最后內(nèi)存溢出。因此,我的建議是最好不用一對(duì)多,凡是有一對(duì)多的需求全部采用分頁(yè)查詢的方式解決。

  最后,大多數(shù)ORM對(duì)級(jí)聯(lián)讀取都是采用join的方式,在數(shù)據(jù)量很大的情況時(shí),join操作很慢,而且無(wú)法水平分割數(shù)據(jù)庫(kù)表。對(duì)讀取要求很高的應(yīng)用,最好不要設(shè)置級(jí)聯(lián)讀取。

  緩存

  絕大多數(shù)ORM都會(huì)提供緩存,通常還分為一級(jí)緩存和二級(jí)緩存。一級(jí)緩存只在當(dāng)前會(huì)話內(nèi)有效,當(dāng)我們?cè)谝粋€(gè)會(huì)話里反復(fù)讀取同一個(gè)對(duì)象時(shí),只有第一次ORM會(huì)從數(shù)據(jù)庫(kù)中讀取,后續(xù)請(qǐng)求會(huì)直接從緩存中讀取,例如:

  view sourceprint?1.int id=12345;

  2.User u1 = load(User.class, id); // 從數(shù)據(jù)庫(kù)讀

  3.User u2 = load(User.class, id); // 從一級(jí)緩存讀

  4.System.out.println(u1==u2); // True

  實(shí)際上,很少有人會(huì)寫出這樣的代碼,所以,一級(jí)緩存幾乎沒有什么作用。

  而二級(jí)緩存就作用于整個(gè)應(yīng)用。不過(guò),當(dāng)數(shù)據(jù)量很小的時(shí)候,通過(guò)增大數(shù)據(jù)庫(kù)服務(wù)器的內(nèi)存就和使用緩存沒什么區(qū)別,當(dāng)數(shù)據(jù)量非常大的時(shí)候,二級(jí)緩存的命中率很低,原因當(dāng)然是緩存大小不夠,因此,針對(duì)海量數(shù)據(jù)通常都要自己動(dòng)手,用memcached做專門的緩存服務(wù)器來(lái)提高性能。所以,二級(jí)緩存不開也罷。

  確定事務(wù)范圍,小心使用Lasy Loading

  使用ORM也需要對(duì)數(shù)據(jù)庫(kù)事務(wù)有一定了解,通常ORM的一個(gè)會(huì)話對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù)事務(wù),如果事務(wù)持續(xù)時(shí)間長(zhǎng),占用的數(shù)據(jù)庫(kù)資源就長(zhǎng),數(shù)據(jù)庫(kù)并發(fā)處理能力就會(huì)降低,所以,事務(wù)范圍越短越好。對(duì)于Web應(yīng)用,把事務(wù)限制在Controller中就比限制在Controller+View中要短,通過(guò)MVC框架提供的各種攔截器可以很方便地開啟和關(guān)閉事務(wù)。當(dāng)事務(wù)限制在Controller時(shí),到了View渲染的時(shí)候,就無(wú)法使用LasyLoading功能了。Lasy Loading雖然簡(jiǎn)單,但不當(dāng)使用也會(huì)造成嚴(yán)重的性能問(wèn)題,所以還是不用為妙。

  以上對(duì)ORM框架的使用做了一個(gè)簡(jiǎn)單的概括,實(shí)際應(yīng)用中還需要通過(guò)大量實(shí)踐慢慢探索。

  Write your comment

  Before write your comment, please sign on.

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多