日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

如何高效使用JavaEE ORM框架

 集微筆記 2013-10-25

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

  ORM的原理

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

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

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

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

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

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

  正確使用CRUD

  正因為我們需要時刻區(qū)分一個對象的持久化狀態(tài),所以,編寫CRUD(Create,Retrieve,Update,Delete的縮寫)要嚴(yán)格遵循以下原則:

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

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

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

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

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

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

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

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

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

  05. // TODO: ...

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

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

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

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

  10. update(p);

  11.}

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

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

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

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

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

  5. // TODO: ...

  6. // 刪除:

  7. delete(p);

  8.}

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

  級聯(lián)讀取

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

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

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

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

  緩存

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

  view sourceprint?1.int id=12345;

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

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

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

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

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

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

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

  以上對ORM框架的使用做了一個簡單的概括,實際應(yīng)用中還需要通過大量實踐慢慢探索。

  Write your comment

  Before write your comment, please sign on.

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多