由于EJB3.0规范中已经采用JPA规范下了技术,CMP便显得不那么流行了,在此我引用一篇文章以做了解。
 
学习预备:EJB2.X背景知识,了解EJB容器相关知识。
学习目标:通过本文学习,能看懂采用CMP规范或者技术的应用代码,了解2.x规范下的持久层方案及技术。
 
 
引文如下:
==============================
 
EJB有两种主要类型BMP(Bean managed persistence )和CMP(Container managed persistence ),这两种类型各有优缺点。

BMP是在Bean中完成对数据库JDBC的各种调用,也就是说,在你的实体bean(entity bean)中,明确写入了SQL语句,如"insert .. "或"select ..",并且使用Datasource获得一个数据库资源以及连接(connection)从而对数据库直接进行增加删除修改。

CMP是由EJB容器自动完成对数据库的操作,你所有做的,就是在实体bean重写入SetXXX或getXXX方法,然后在ejb-jar.xml中定义 cmp-field就可以。

很明显,CMP编写要简单多,而且数据库操作由EJB容器完成应该是一种趋势,但是CMP有个缺点就是不够灵活,如果我们要完成类似SQL搜索语句的like命令,如"select * from A where name like '%banqiao'",CMP就无法自动帮助我们完成,这样我们就需要BMP自己来写。

在实际应用,一般为了效率考虑,我们尽量使用CMP,但如何为将来有可能使用BMP作好准备,就是说有可以延伸到BMP的基础。EJB 2.0对CMP的抽象类支持为我们提供了这种实现的基础。

总体思路是,先使用 抽象类完成 CMP 如果需要 BMP 可以 extend这个抽象类,然后覆盖原来的方法(用自己的特殊SQL语句操作来覆盖该方法)。

 
以Java 宠物店(Java Pet Store Demo 1.3)中的地址实体bean:AddressEJB为例:
 
public abstract class AddressEJB implements EntityBean {        
         private EntityContext context = null;        
    
         // getters and setters for PO CMP fields        
    
         public abstract String getFirstName();        
         public abstract void setFirstName(String name);        
         public abstract String getLastName();        
         public abstract void setLastName(String name);        
         public abstract String getStreet1();        
         public abstract void setStreet1(String name);        
         public abstract String getStreet2();        
         public abstract void setStreet2(String name);        
         public abstract String getCity();        
         public abstract void setCity(String name);        
         public abstract String getState();        
         public abstract void setState(String name);        
         public abstract String getCountry();        
         public abstract void setCountry(String name);        
         public abstract String getZip();        
         public abstract void setZip(String name);        
    
         public Object ejbCreate(        
                String fName,        
                String lName,        
                String s1,        
                String s2,        
                String cy,        
                String st,        
                String cnty,        
                String pcode)        
                 throws CreateException {        
                setFirstName(fName);        
                setLastName(lName);        
                setStreet1(s1);        
                setStreet2(s2);        
                setCity(cy);        
                setState(st);        
                setCountry(cnty);        
                setZip(pcode);        
                 return null;        
        }        
    
         public void ejbPostCreate(        
                String fName,        
                String lName,        
                String street1,        
                String street2,        
                String city,        
                String state,        
                String country,        
                String zip)        
                 throws CreateException {        
        }        
         public void setEntityContext(EntityContext c) {        
                context = c;        
        }        
         public void unsetEntityContext() {        
        }        
         public void ejbRemove() throws RemoveException {        
        }        
         public void ejbActivate() {        
        }        
         public void ejbPassivate() {        
        }        
         public void ejbStore() {        
        }        
         public void ejbLoad() {        
        }        
}    
 
在上面的AddressEJB中,我们看到只有setXXX或getXXX的方法。

在相应的部署描述文件ejb-jar.xml中我们看到:
 
<entity>        
  <display-name>AddressEJB</display-name>        
  <ejb-name>AddressEJB</ejb-name>        
  <local-home>com.sun.j2ee.blueprints.address.ejb.AddressLocalHome</local-home>        
  <local>com.sun.j2ee.blueprints.address.ejb.AddressLocal</local>        
  <ejb- class>com.sun.j2ee.blueprints.address.ejb.AddressEJB</ejb- class>        
  <persistence-type>Container</persistence-type>        
  <prim-key- class>java.lang.Object</prim-key- class>        
  <reentrant>False</reentrant>        
  <cmp-version>2.x</cmp-version>        
  < abstract-schema-name>Address</ abstract-schema-name>        
    
    
  <cmp-field>        
    <field-name>firstName</field-name>        
  </cmp-field>        
  <cmp-field>        
    <field-name>lastName</field-name>        
  </cmp-field>        
  <cmp-field>        
    <field-name>street1</field-name>        
  </cmp-field>        
  <cmp-field>        
    <field-name>street2</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>country</field-name>        
  </cmp-field>        
  <cmp-field>        
    <field-name>zip</field-name>        
  </cmp-field>        
    
    
  <security-identity>        
    <description></description>        
    <use-caller-identity></use-caller-identity>        
  </security-identity>        
    
</entity>    
 
在上面部署文件中,标明了Address数据库字段:

firstName,lastName,street1,street2,city,state,country,zip

一旦我们要使用BMP, 只要 继承上面的CMP bean:

public class AddressBeanBMP extends AddressEJB {

用我们自己的BMP方法覆盖AddressEJB中的方法:

ejbLoad() -->从数据库中获取数据(SELECT)
ejbStore() -->修改数据库数据UPDATE)
ejbRemove() -->删除数据库数据(DELETE)
ejbCreate() -->插入新的数据记录(INSERT)
ejbFindByPrimaryKey(primary key) --> 确保 primary key 存在.
ejbFindAllPrimaryKey() -->自己的定义 返回一个primary key所有数据记录的collectionxiam

下面以ejbCreate()为例:
public Object ejbCreate(        
        String fName,        
        String lName,        
        String s1,        
        String s2,        
        String cy,        
        String st,        
        String cnty,        
        String pcode)        
         throws CreateException {        
    
         // insert row into database        
         this.fName = fName;        
         this.lName = lName;        
         this.s1 = s1;        
         this.s2 = s2;        
         this.cy = cy;        
         this.st = st;        
         this.cnty = cnty;        
         this.pcode = pcode;        
    
         // Insert database record        
         try {        
                Connection connection = getConnection();        
                PreparedStatement statement =        
                        connection.prepareStatement(        
                                 "INSERT INTO Address (firstName,lastName,street1,street2,city,state,country,zip) VALUES (?, ?, ?,?,?,?)");        
                statement.setString(1, fName);        
                statement.setString(2, lName);        
                statement.setString(3, pcode);        
                statement.setString(4, s1);        
                statement.setString(5, s2);        
                statement.setString(6, st);        
                statement.setString(7, cy);        
                statement.setString(8, cnty);        
                 if (statement.executeUpdate() != 1) {        
                        statement.close();        
                        connection.close();        
                         throw new CreateException( "Could not create: ");        
                }        
                statement.close();        
                connection.close();        
        } catch (SQLException e) {        
                 throw new EJBException( "Could not create: ");        
        }        
}    
 
============================================================
 
其实,在我前面文章中写的EJB2.1 Files List中的SLSB及EntityBean的文件结构也能看出,流行的做法甚至可以对Bean进行再封装,这样可以提高扩展性。
 
当然对于Entity bean本文采用的做法是具有很好的灵活性的,值得借鉴学习。比较JPA规范其最大的不足个人认为在于敏捷性上,所以个人比较赞同JPA的使用。