承接設計模式 4,本文示範如何為 Company(公司)與 Employee(員工)實體 Bean 建立一對多雙向關聯,重點解析 CMP 欄位調整、Deployment Descriptor 映射、以及透過 ejbLoad()/ejbStore() 同步外鍵與遠程引用的核心機制。
兩者均繼承對應結構類(CompanyStruct/EmployeeStruct),實作 EntityBean 介面。
public class CompanyBean extends CompanyStruct implements EntityBean {
EntityContext entityContext;
// CMP 欄位自動對應 CompanyStruct 所有屬性
public java.util.Collection employees; // 一對多關聯端
public java.util.Collection getEmployees() {
return employees;
}
// getData()/setData() 等標準方法...
}
public class EmployeeBean extends EmployeeStruct implements EntityBean {
EntityContext entityContext;
// CMP 對應 EmployeeStruct 所有欄位,**除 comId 外**
public Company company; // 遠程引用 Company,取代 comId 成為 CMP
}
EmployeeStruct 原有 comId(外鍵)作為 CMP。company 遠程引用為 CMP。getData()/setData() 僅處理 comId,如何同步 company 引用?容器在交易開始/結束時自動呼叫,用於將資料庫狀態與 Bean 記憶體狀態雙向同步。
public void ejbLoad() {
try {
// 從 company 遠程引用取得主鍵,回填 comId(非 CMP 欄位)
comId = (company == null) ? null : (Integer) company.getPrimaryKey();
} catch (Exception e) {
throw new EJBException(e);
}
}
comId 由 company 反向導出。getData() 回傳的結構即含正確 comId。public void ejbStore() {
try {
// 將 comId 轉換為 company 遠程引用(寫入資料庫前)
company = (comId == null) ? null :
beanGlossary.getCompanyHome().findByPrimaryKey(comId);
} catch (Exception e) {
throw new EJBException(e);
}
}
comId 解析為 company 遠程引用。comId 為 Company 主鍵,透過 CompanyHome.findByPrimaryKey() 取得遠程介面。public class BeanGlossarySB implements SessionBean {
private Context context;
private CompanyHome companyHome;
public CompanyHome getCompanyHome() throws NamingException {
if (companyHome == null) {
companyHome = (CompanyHome) PortableRemoteObject.narrow(
getContext().lookup("java:comp/env/ejb/Company"), CompanyHome.class);
}
return companyHome;
}
// 其他 EJB Home 快取...
}
EmployeeBean 定義 findEmployeesByCompany(Company) finder,回傳員工遠程引用集合。CompanyBean 的 employees CMR 欄位映射至該 finder。company.getEmployees() 即可取得關聯員工集合。模式 5 展示了 EJB 2.x CMP/CMR 進階映射:以遠程引用取代外鍵成為 CMP,配合 ejbLoad/ejbStore 生命週期回調實現雙向同步。此模式雖增加生命週期方法邏輯,但讓 Bean 模型更貼近物件導向設計,減少結構類與資料庫 schema 的阻抗失配。