首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

EJB 持久化技術深度解析:CMP vs BMP、EJB QL 與 JBoss 實戰部署

     編輯:Java 相關知識

核心矛盾:關係表格 vs 對象樹

關係數據庫表格存儲 vs Java 對象樹狀結構,結構不匹配催生對象持久化方案。EJB 通過容器管理持久化 (CMP) 提供標準化解決路徑。

EJB 運行時架構

  • EJB 容器:管理生命周期、事務、安全、連接池、實例池、遠程訪問
  • 好萊塢原理:容器回調 Bean,而非 Bean 主動調用容器
  • 客戶端隔離:通過容器代理訪問 Bean,不直接持有引用

三類 EJB 對比

Entity Bean 兩種持久化模式

BMP (Bean Managed Persistence)

  • 開發者手寫 JDBC/SQL 實現 ejbLoad/ejbStore/ejbFind 等
  • 靈活性高,但代碼量大、移植性差、容易出錯

CMP (Container Managed Persistence) — 推薦

  • 容器自動生成 SQL,字段映射通過部署描述符聲明
  • 數據訪問邏輯與數據源解耦,跨應用服務器可移植
  • 性能由容器優化 (延遲加載、預取、批量更新)

實戰:UserEJB 完整開發部署 (JBoss 3.0.2 + Hypersonic SQL)

1. 接口定義 (本地接口)

// LocalUser.java
public interface LocalUser extends EJBLocalObject {
    String getUserID(); // PK
    String getFullName(); void setFullName(String);
    String getAddress(); void setAddress(String);
    String getCity(); void setCity(String);
    String getState(); void setState(String);
    String getZip(); void setZip(String);
}

// LocalUserHome.java
public interface LocalUserHome extends EJBLocalHome {
    LocalUser create(String userID, String fullName, String address, String city, String state, String zip) throws CreateException;
    Collection findByFullName(String fullName) throws FinderException;
    LocalUser findByPrimaryKey(String userID) throws FinderException;
}

2. Bean 類 (實現 EntityBean)

public class UserEJB implements EntityBean {
    public String USERID, FULLNAME, ADDRESS, CITY, STATE, ZIP;
    private EntityContext ctx;

    // CMP 字段 getter/setter (容器調用)
    public String getUserID() { return USERID; }
    public void setUserID(String id) { USERID = id; }
    // ... 其他字段同理

    // 生命周期回調
    public String ejbCreate(String userID, String fullName, String address, String city, String state, String zip) {
        setUserID(userID); setFullName(fullName); setAddress(address); setCity(city); setState(state); setZip(zip);
        return userID; // 返回主鍵
    }
    public void ejbPostCreate(...) {} // 無拋出 CreateException
    public void setEntityContext(EntityContext ctx) { this.ctx = ctx; }
    public void unsetEntityContext() { ctx = null; }
    public void ejbActivate() {} public void ejbPassivate() {}
    public void ejbLoad() {} public void ejbStore() {} public void ejbRemove() {}
}

3. 標準部署描述符 (ejb-jar.xml)

<entity>
  <ejb-name>UserEJB</ejb-name>
  <local-home>com.jeffhanson.datatier.ejb.LocalUserHome</local-home>
  <local>com.jeffhanson.datatier.ejb.LocalUser</local>
  <ejb-class>com.jeffhanson.datatier.ejb.UserEJB</ejb-class>
  <persistence-type>Container</persistence-type>
  <prim-key-class>java.lang.String</prim-key-class>
  <reentrant>False</reentrant>
  <cmp-field><field-name>USERID</field-name></cmp-field>
  <cmp-field><field-name>FULLNAME</field-name></cmp-field>
  <cmp-field><field-name>ADDRESS</field-name></cmp-field>
  <cmp-field><field-name>CITY</field-name></cmp-field>
  <cmp-field><field-name>STATE</field-name></cmp-field>
  <cmp-field><field-name>ZIP</field-name></cmp-field>
  <primkey-field>USERID</primkey-field>
</entity>
<assembly-descriptor>
  <container-transaction>
    <method><ejb-name>UserEJB</ejb-name><method-name>*</method-name></method>
    <trans-attribute>Required</trans-attribute>
  </container-transaction>
</assembly-descriptor>

4. JBoss 特定映射 (jaws.xml)

<jaws>
  <datasource>java:/DefaultDS</datasource>
  <type-mapping>Hypersonic SQL</type-mapping>
  <enterprise-beans>
    <entity>
      <ejb-name>UserEJB</ejb-name>
      <create-table>true</create-table>
      <table-name>UserEJB</table-name>
      <cmp-field>
        <field-name>USERID</field-name>
        <column-name>USERID</column-name>
      </cmp-field>
      ... 其他字段映射 ...
    </entity>
  </enterprise-beans>
</jaws>

5. 客戶端訪問 (UserService)

public class UserService {
    private static LocalUserHome getHome() throws NamingException {
        InitialContext ctx = new InitialContext();
        return (LocalUserHome) ctx.lookup("local/UserEJB"); // 本地接口查找
    }
    public static UserInfo getUser(String userID) {
        LocalUserHome home = getHome();
        LocalUser user = home.findByPrimaryKey(userID);
        if (user == null) user = home.create(userID, "John " + userID + " Doe", ...);
        return convert(user);
    }
    public static UserInfo[] getUsersByState(String state) {
        Collection list = getHome().findByState(state);
        // 迭代轉換...
    }
}

EJB QL:面向對象查詢語言

容器將 EJB QL 轉換為 SQL。在 jaws.xml 中定義查詢映射:

<query>
  <query-method>
    <method-name>findByState</method-name>
    <method-params><method-param>java.lang.String</method-param></method-params>
  </query-method>
  <ejb-ql><![CDATA[
    SELECT DISTINCT OBJECT(u) FROM UserEJB u WHERE u.STATE = ?1
  ]]></ejb-ql>
</query>
  • OBJECT(u):返回實體對象而非標量
  • ?1:對應 findByState(String) 第一個參數
  • 內置 findAll/findByPrimaryKey 無需定義

總結

EJB CMP 通過聲明式映射、容器生成 SQL、標準化 EJB QL,有效橋接對象與關係世界。在 JBoss 上配合 jaws.xml 完成數據源、表創建、字段映射配置,可快速構建可移植的持久化領域模型。合理使用 CMP 可大幅降低持久層開發複雜度,讓團隊聚焦業務邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
類型通信狀態持久化典型用途
Message Driven (MDB)異步 (JMS)無無事件處理、異步任務
Session Bean同步有/無狀態不持久化業務流程控制
Entity Bean同步共享、主鍵、事務BMP / CMP領域數據建模