国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

利用實體EJB來避免性能缺陷:一種映射持久性數據的簡便方法

2019-11-18 14:59:14
字體:
來源:轉載
供稿:網友

  EJB (Entity EnterPRise javaBeans) 是一種可以把持久性數據映射到Java組件上的簡便
方法。CMP (Container-Managed persistence)提供了快速開發功能,這是因為EJB 容器
可自動處理持久性數據的加載和存儲。然而,在具有許多優點的同時,假如Entity EJB沒有
正確使用,也會導致性能的大幅下降。本專欄具體介紹了幾個常見的編程缺陷,它們經常使
EJB的程序員犯錯,并妨礙其實體(Entity)beans的性能。
Primary Key類
類似于數據庫中的行,實體beans有一個主鍵(primary key)與它關聯。這個主鍵可以是實
體bean的一個單一字段。在這種情況下,實體bean可以用字段的類作為主鍵。

還可能提供一種自定義的主鍵類。對于復合主鍵來說,必須定制一個主鍵類,來映射多個實
體bean的字段。

使用定制的主鍵類,開發人員必須實現hashCode和equals方法。因為EJB容器常在其內部數
據結構中使用主鍵類,所以這個類必須正確和有效的實現hashCode和equals方法 (參見清
單1)。


清單 1:

一個低效但正確的主鍵類
public class MyPk
implements java.io.Serializable
{
public String str;
public int i;
public byte b;
public MyPk() {}
public int hashCode() { return -1; }
public boolean equals(Object o) {
if ((o != null) && (MyPk.class.equals(o.getClass()))) {
MyPk other = (MyPk) o;
return other.str.equals(str) && other.i == i && other.b == b;
} else {
return false;
}
}
}


實現hashCode方法
hashCode方法對于兩個equal的對象,必須返回相同的值,而且應該相對均勻地分配哈希值
。下面顯示的第一種實現方法正確而有效,但是根本沒有分配哈希值。這個hashCode實現把
全部哈希表變換到一個列表中,而且必須線性檢索。顯然,這樣違反了可檢索性數據結構的
設計初衷。


private int hash = -1;
public int hashCode() {
if (hash == -1) {
hash = str.hashCode() ^ i ^ b;
}
return hash;
}


上面的hashCode實現計算了字符串的哈希值和原字段的異或(XOR)值。 與其它的邏輯運算
符相比,諸如AND和OR,XOR應該是更可取的,因為它可以更好地分配哈希值。這種實現還可
以把哈希值緩存在一個成員變量中,以避免重復計算這個值。

實現Equals 方法
equals方法的功能是使用傳入的參數比較當前對象,假如對象有相同的值,就返回true。默
認的java.lang.Object.equals用于比較引用(指針)值,假如它們相等就返回true。對于
大多數的主鍵類,需要重寫這個方法,以便在主鍵類中比較這些值(參見清單 2)。


清單:2

一個有效的equals實現
public final class MyPk ...
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof MyPk) {
MyPk other = (MyPk) o;
return other.hashCode() == hashCode() &&
other.i == i && other.b == b &&
other.str.equals(str);
} else {
return false;
}
}


這是一種優化的equals實現,它的第一行用與此相反的方式比較傳入的引用。第一,雖然這
看起來有點生疏,但這是EJB容器檢查一個主鍵是否已經在它的數據結構中存在的常用方法


第二,我們已經用一個更有效的檢查實例替代了getClass().equals。假如傳入參數的類是
MyPk類或它的一個子類,操作符的實例將返回true。 用final修飾MyPk類,這樣創建的方法
可以安全地使用操作符的實例,因為這樣就不存在子類了。

最后,比較哈希表和成員變量。Java中的表達式具有短路功能,這意味著假如第一個表達式
是false,第二個表達式將不再計算。這個equals方法很好的利用了這一點,先用最簡易的
比較調整了and語句的順序。在這個例子里,首先比較的是哈希值,這是因為我們的實現緩
存了這個值,而且發生兩個對象具有相同的hashCode但卻不相等的情況很少。接下來比較的
是原始字段;最后是調用花費資源最多的java.lang.String.equals。

加載和存儲實體beans
WebLogic Server的EJB容器提供兩種不同的實體bean類型:Read-Only 實體beans 和Read-
Write 實體beans。Read-Only 實體beans 支持高級群集緩存,關于這一點,將會在另一個
專欄中討論。

實體beans是具有事務功能的對象。理解事務和持久性之間的關系是很重要的。當實體bean
的一個實例首次在事務中使用時,將會從數據庫來刷新它的狀態。對實體bean狀態的任何修
改會馬上刷新到數據庫中,而且會是在事務提交之前完成(在beforeCompletion回調中,詳
細請參見 javax.transaction.Synchronization接口)。

讓我們來看一個Employee 實體bean的例子,它具有所需的事務屬性。可以假設我們在一個
單獨的事務中創建了這個bean,而且當前它有一個指向Employee實例的引用。


Employee e = ...
String name = e.getName();
e.setSalary(e.getSalary() * 2);
e.setLevel(e.getLevel() + 1);


從上面的代碼片斷中得出的重要發現是調用者沒有啟動事務。因為bean是使用必需的事務屬
性來部署的,每個方法都作為一個單獨的事務運行,并加載、存儲到數據庫中。在這個例子
中,每一個getXXX方法導致了一次數據庫讀操作,而每個setXXX方法導致了一次數據庫讀操
作和一次數據庫更新操作。這個簡單的例子里有五個單獨的事務,并有五次數據庫讀操作(
SELECTs)和兩次更新操作。

Employee e = ...
tx.begin();
String name = e.getName();
e.setSalary(e.getSalary() * 2);
e.setLevel(e.getLevel() + 1);
tx.commit();


上面這個代碼樣例用單個事務包裝了對Employee 實體bean的調用。在這個示例代碼中使用
了一個UserTransaction的引用,用手工方式開始并提交事務。這些功能也可以用一個sess
ion bean來實現,它需要使用一個可用容器治理的事務,然后在容器事務中調用實體beans


當所有的業務方法都在一個單獨的事務中執行時,只存在兩種數據庫訪問。第一個getName
調用產生了一個從數據庫加載的SELECT語句。隨后的getXXX和setXXX 方法沒有導致任何數
據庫訪問,因為它們在同一個事務中,而且數據已經被加載了。在事務提交時,將執行一個
UPDATE語句向數據庫寫入新的"salary"和新的"level"。

使用強制性事務屬性

許多EJB的程序員用"Required"級別的事務屬性來部署他們的beans。"Required"級別的事務
屬性在多數情況下都能起作用,因為假如存在一個調用者的事務,它將繼續這個事務;否則
它將啟動它自己的事務。正如我們前面看見的,開發人員必須理解:事務屬性是如何強烈影
響性能的。小組中的新程序員可能會在一個單獨的事務中重用Employee 實體bean,并錯誤
地調用每個 getXXX方法。一種避免這種情況的方法是使用強制性(Mandatory)事務屬性來
部署你的實體bean。和"Required"級別的事務屬性不同,強制性(Mandatory)bean不會啟
動它自己的事務。假如它和一個事務被同時調用,強制性bean將會參與到這個事務中。假如
強制性(Mandatory)bean沒有和某個事務一起調用, EJB容器會立即向調用者拋出一個異
常。

用強制性(Mandatory)事務屬性的來部署實體beans是一種輕易的方法,它指出應該將這些
操作組織到一個調用事務中。

CMP的優勢 - 加載Beans 的Finder

與業務方法一樣,實體bean的finder方法的性能依靠于對事務的正確設置和使用。讓我們來
考慮使用Employee Bean的另一個例子。


Collection employees =
home.findEmployeesWithSalariesGreaterThan(30000);
Iterator it = employees.iterator();
while (it.hasNext()) {
Employee e = (Employee) it.next();
double salary = e.getSalary();
}


這個簡單的例子執行了一個finder(數據查詢),用于返回其薪金大于傳入參數(在本例中
,轉入的參數為30,000)的雇員。然后從頭到尾重復這個搜索過程,并從每個雇員中檢索其
薪金。在finder返回了N個雇員的情況下,該例子將訪問N+1次數據庫。finder訪問了數據庫
,隨后的每一個getSalary回調都訪問了數據庫。

到現在,你可能已經猜測出:我們試圖在一個單獨的事務中執行finder和getSalary方法。
現在,有多少次對數據庫的訪問呢?答案可能會令你驚異。

假如Employee是一個CMP 實體bean,而且finder和隨后的getSalary方法發生在同一個事務
中,那么只存在一次數據庫訪問。finder執行了一次選擇查詢,這次查詢加載了對應的主鍵
和其它的Employee字段。這些預取出的beans被輸入到EJB緩存中,而且getSalary方法直接
從內存緩存中調用讀操作。這就是weblogic-ejb-jar.xml中的finders-load-bean選項。默
認狀態下它是啟用的。而且它答應finder從EJB緩存中預取出附加的數據。在這個常見的例
子中,對數據庫訪問從N+1次減少到了1次。

假如Employee類是BMP 實體bean將會怎樣呢?令你吃驚的是,假如這樣就還會有N+1次數據
庫采樣。假如Employee是一個BMP實體bean,該如何呢?這也許會令你驚異,但它對數據庫
的訪問仍然是N+1次。BMP 實體bean會在bean類中執行一個ejbFindEmployeesWithSalarie
sGreaterThan方法,以返回容器的一個主鍵集合,但它不會預取出到緩存中。然后每個get
Salary調用都會產生一個ejbLoad調用和另一個數據庫訪問。

Finder預取出beans是CMP在性能上的一大優勢,一般來說,CMP(非凡是EJB 2.0的CMP) 實
體beans的性能要優于BMP 實體beans。

結束語
我希望這些技巧可以幫助您在實體beans之中獲得更好的性能。我們還會在即將刊載的其它
專欄中討論WebLogic Server 6.1 和EJB 2.0 中的實現技巧和新特性。


關于作者

Rob Woollen是BEA Systems公司WebLogic Server開發小組中的一位資深軟件工程師。他擁
有普林斯頓大學的計算機科學學士學位。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宝山区| 佳木斯市| 赣榆县| 银川市| 车致| 信阳市| 崇信县| 晋中市| 金塔县| 东港市| 涪陵区| 北碚区| 海淀区| 奉新县| 和平县| 偏关县| 南溪县| 潼南县| 琼海市| 绿春县| 措美县| 小金县| 滨州市| 顺昌县| 杭锦旗| 马尔康县| 丰城市| 织金县| 奇台县| 阳信县| 宜章县| 石阡县| 湾仔区| 马山县| 浪卡子县| 金阳县| 宜黄县| 阜南县| 濮阳县| 辽阳县| 司法|