在这篇文章中,我们将讨论:
1.CMP 2.0: 发生了什么变化?
2.Inventory(货物清单)EJB应用程序
3.开发一个CMP bean
4.移植CMP bean到BMP
CMP 2.0: 发生了什么变化?
当EJB问世的时候,CMP引起了很大的反响,当EJB2.0问世的时候,又对CMP模型进行了修订,它给了我们真正想要的特性:诸如关系和查询语言标准化。
与EJB1.1相比,当我们用EJB2.0写一个CMP bean时,我们的写法有很大的不同。我们创建抽象类,而不是创建被容器管理变量的公共域,我们像JavaBean一样创建抽象的属性(getters and setters)。这准许特定厂商的持久性管理器用他们自己的方式实现数据访问器(accessors)。
这将帮助他们(厂商)提出像这样的逻辑:
●由于他们不调用任何set方法,所以在ejbStore()中不做任何动作
●他们仅仅改变一个域,所以我们只在UPDATE查询语句中set那个域
●我们延迟装载一些数据,所以当用户用GET方法请求数据的时候我们才读取他
顺便提一个问题:为什么我们必须在抽象类中创建抽象方法?为什么持久性管理器不能在派生类中创建方法?
答:我们必须在抽象类中访问这些方法。例如:在ejbCreate()中,我们通过传入参数来SET它们。
Inventory EJB 应用程序
为了说明该实体模型,我们将看到一个简单的应用用该实体模型化该货物清单(Inventory)系统。我们应用程序包括以下组成部分:
Inventory实体Bean: 这是我们的焦点。他将映射到数据库表Inventory,该表纪录了条目的名字(主关键字),价格和仓库中条目的数目
价格无状态会话Bean:该Bean用Inventory实体Bean来得到条目的价格,它用本地(local)接口来访问该实体
价格客户: 这个命令行应用程序在会话(session)上运行该方法来测试所有工作都运行得很好
关键点是当我们把这个Inventory实体从 CMP移植到BMP实现时候,什么也不要做改变
让我们看一下用CMP实现Inventory实体
开发一个CMP bean
inventory CMP要我们创建一个抽象类(依照实体说明),XML描述文件告诉框架应该影射什么,图一说明了我们的条目看起来是什么样子
抽象实体bean
该抽象实体有下列属性:
实现了javax.ejb.EntityBean接口
abstract public class InventoryBean implements EntityBean |
实现在实体接口中声明的方法
public void setEntityContext(EntityContext context) { ctx = context; } public void unsetEntityContext() { ctx = null; } public void ejbActivate() {} public void ejbPassivate() {} public void ejbRemove() throws RemoveException {} public void ejbStore() {} public void ejbLoad() {} |
实现ejbCreate()和ejbPostCreate()方法该方法对应于HOME接口中的Create()方法(用抽象set方法set所有参数)
public String ejbCreate(String item, float price, int stock) throws CreateException { setItem(item); setPrice(price); setStock(stock); return null; } |
public void ejbPostCreate(String item, float price, int stock) throws CreateException {}
实现抽象get和set方法
public abstract String getItem(); public abstract void setItem(String item); public abstract float getPrice(); public abstract void setPrice(float price); public abstract int getStock(); public abstract void setStock(int stock); |
实现一个助手方法来访问实体上下文(在后面的BMPbean中我们将用到它)
public EntityContext getEntityContext() { return ctx; } |
EJB部署描述符
我们创建了一个CMP实体类,现在该创建部署描述符了.首先我们将创建一个标准的"ejb-jar.xml",然后我们需要配置与厂商有关的信息.我们将说明WebLogic 6.1 部署描述符,我们选一个.
标准 ejb-jar.xml
基本的实体配置信息:
我们将配置类名,用局部变量,在实体内部它们是局部变量.我们通过持久类型XML标签告诉容器该实体是CMP
<entity>
<ejb-name>InventoryBean</ejb-name>
<local-home>InventoryHome</local-home>
<local>Inventory>/local<
<ejb-class>InventoryBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
接着,我们将告诉容器那些域是容器管理的,item域是主键类:
特定厂商的weblogic-ejb-jar.xml
我们首先在weblogic-ejb-jar.xml中定义厂商特定的信息。在这里我们告诉服务器在哪儿存放本地存根(stub)和哪儿去查找CMP映射。
持久映射信息:
<persistence-type>
<type-identifier>WebLogic_CMP_RDBMS</type-identifier>
<type-version>6.0</type-version>
<type-storage>META-INF/weblogic-cmp-rdbms-jar.xml</type-storage>
</persistence-type>
本地JNDI名字
<local-jndi-name>InventoryHome</local-jndi-name>
特定厂商的weblogic-cmp-rdbms-jar.xml
数据源(datasource)名称:
(这个数据源在配置的时候被创建)
<data-source-name>InventoryDB</data-source-name>
表(table)名:
<table-name>inventory</table-name>
域(field)映射
<field-map>
<cmp-field>stock</cmp-field>
<dbms-column>stock</dbms-column>
</field-map>
<field-map>
<cmp-field>item</cmp-field>
<dbms-column>item</dbms-column>
</field-map>
<field-map>
<cmp-field>price</cmp-field>
<dbms-column>price</dbms-column>
</field-map>
现在我们有了我们想要得一切。容器将利用抽象类和部署信息来使一切工作的很好。由于某些原因,我们可能让这个CMPbean移植到BMP。以下是为什么我们要这样做的原因
1.可移植性:当前,它只工作在WebLogic 6.1,,而我们并不想学习其他的工具。这非常正常如果我们想卖这个数据库组件的话--它能够运行在任何服务器上。
2.性能:如果我们想很好的运行SQL(用存储过程,表的联合等等)。
3.可替换的数据源:如果我们想访问不同的数据源而不是RDBMS。
用BMP移植Bean
这里我们将讨论移植模式。我们可以通过“变成持久管理器“模式将CMP移植到BMP。我们可以看到持久管理器扩展了我们创建的抽象类并实现了抽象类的方法!最终的设计看起来如图二:
BMP实体bean
让我们看看这个即将成为BMPbean的bean类:
扩展抽象CMPbean类:
public class InventoryBeanBMP extends InventoryBean {}
创建映射域:
public String item;
public float price;
public int stock; 改写ejb*()系列方法:
为了改写EJB方法,我们需要记住在BMP中我们对每一个方法所负的责任。
大多数项目看起来是一样的,所以让我们看其中一个方法(你可以在所下载的代码中看到其余代码)
public String ejbCreate(String item, float price, int stock) throws CreateException {
// insert row into database
this.item = item;
this.price = price;
this.stock = stock;
// 插入数据库记录
try {
Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement
("INSERT INTO inventory (item, price, stock) valueS (?, ?, ?)");
statement.setString(1, item);
statement.setFloat(2, price);
statement.setInt(3, stock);
if (statement.executeUpdate() != 1) {
statement.close();
connection.close();
throw new CreateException("Could not create: " + item);
}
statement.close();
connection.close();
return item;
}
catch(SQLException e) {
throw new EJBException("Could not create: " + item, e);
}
}
实现get 和set方法:
public String getItem() {
return this.item;
}
public void setItem(String item) {
this.item = item;
}
public float getPrice() {
return this.price;
}
public void setPrice(float price) {
this.price = price;
}
public int getStock() {
return this.stock;
}
public void setStock(int stock) {
this.stock = stock;
}
辅助函数得到一个JDBC连接:
private Connection getConnection() throws SQLException {
DataSource ds = null;
try {
Context ctx = new InitialContext();
ds = (DataSource) ctx.lookup ("java:comp/env/jdbc/InventoryDB") ;
} catch (NamingException exp) {
exp.printStackTrace() ;
}
return (ds == null) ? null : ds.getConnection();
}
EJB 部署描述符:
标准ejb-jar.xml
从CMP转换成BMP时要改变部署环境:
class指向BMP类而不是抽象类,我们要让容器知道我们自己管理持久:
<ejb-class>InventoryBeanBMP</ejb-class>
<persistence-type>Bean</persistence-type> 加入JDBC数据源引用:(在上面getConnection()方法中使用)
<resource-ref>
<res-ref-name>jdbc/InventoryDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref> 厂商特定的 weblogic-ejb-jar.xml:
<reference-descriptor>
<resource-description>
<res-ref-name>jdbc/InventoryDB</res-ref-name>
<jndi-name>inventoryDB</jndi-name>
</resource-description>
</reference-descriptor>
我们不必过多地关心厂商特定的文件,正如我们在CMP中所做的。我们需要指定本地JNDI名称(与以前一样),我们需要指出数据源的本地地址,它部署在应用服务器中。我们已经重用了抽象类,改写它满足我们所需要得BMP实现,然后把我们的部署描述符打包
结论:
我们已经研究了能干净利落让CMP转换为BMP实现的模式。我们的BMP实现只是简单的扩展了CMP抽象bean类然后让它运行!注意到我们以相同的逻辑加入了get/set方法以确保我们在ejbStore()中不做任何动作,或者做一个查询。我们仍就要记住尽可能的用CMPbean,BMP做为最后的手段。
部署例程注意事项:
你可以下载例程模式的代码。代码有两部分 BMP 和 CMP(它们有相同的目录),我们提供了一个例子创建脚本(build.cmd)来编译和部署该例程到BEA WebLogic 6.1。我们需要在脚本中改变一些目录路径让它运行。为了测试该应用可以在客户端运行命令:javac PricerClient。请仔细参阅README.txt 文件来得到更多的信息(如建立一个数据库表等)。
Dion Almaer是中间件公司主要技术人员和TheServerSide.Com J2EE 社区的首席架构师.