• 沒有找到結果。

实体 EJB 的开发技术之二——BMP EJB

第 3 章 EJB 技术进阶

3.2 实体 EJB 的开发技术之二——BMP EJB

上面的输出结果说明了在EJB Server/Container 中 可用的 Goods EJB 对象一共有 20 个 刚好等于goods Table 纪录的行数 这是巧合吗?读者不妨考虑一下

3.2 实体 EJB 的开发技术之二——BMP EJB

上一节 我们已经介绍了CMP 模式的 Entity EJB 的开发技术 在这一节中 我们将介 绍BMP 模式的 Entity EJB 的开发技术

3.2.1 BMP EJB 简介

BMP EJB 全名是 Beans Managed Persistence EJB 中文译名为 Bean 自管理模式的 EJB BMP 模式的 EJB 与 CMP 模式的 EJB 最大的不同之处在于 ejbLoad() ejbStore()



第一部分 JSP 技术与 J2EE 技术

EJB 要优越得多 我们可以在编码实现上述方法的同时 添加自己的商业逻辑 这就是 BMP 模式的一个突出之处 扩展性 如果你对于EJB 编程十分熟练的话 你就会发现 BMP 模式带给你的便利远远超过了带给你的不便

我们建议读者在必要的时候采用BMP 模式的 EJB 完成你的开发项目 3.2.2 创建 EJB 工程

开发CMP 模式的 EJB 与开发 BMP 模式的 EJB 有何不同?应该说 在创建 EJB 的框架 时 这两种模式没有什么不同 但是在开发的后期 CMP 模式的 EJB 与 BMP 模式的 EJB 的EJB Class 大不相同 同时 它们的部署描述符也有所不同

在本小节中 我们将利用JBuilder4 创建一个 Entity EJB Modeler Project 这个 Project 的名字为JDBCBean 前面的步骤和开发 Goods EJB(JDBCTest Project)一模一样 读者可以 参照3.2 节的说明

在EJB Entity Bean Modeler Wizard 的第六步 我们需要修改此 EJB 的 Home Interface Remote Interface 的名字 如图 3.15 所示 这个 Entity EJB 的名字仍然是 Goods 映射的 Table 仍然是goods d 代表主键的字段仍然是 id 字段 主键的数据类型为 int

3.15 JBuilder4 的 EJB Entity Bean Modeler Wizard

在图3.15 的窗口中 都设定好以后 单击 Next 按钮 将出现如图 3.16 所示的窗口 在这个窗口中 我们将要设定Entity EJB 的开发模式 本节开发的是 BMP 模式的 Entity EJB 所以我们应该选中Bean managed peresistence 然后单击 Finish JBuilder4 会根据我们的设 定 自动产生Home Interface Remote Interface EJB Class 部署描述符等文件 这样就算 完成EJB 框架的开发工作 下面就来看看 JBuilder4 为我们自动产生的代码



第 3 章 EJB 技术进阶

3.16 JBuilder4 的 EJB Entity Bean Modeler Wizard 3.2.3 Home Interface 和 Remote Interface

读者请看程序清单 3.7 和程序清单 3.8 这就是 BMP 模式的 Goods EJB 的 Home Interface Remote Interface 的源代码 读者不妨把它们和程序清单 3.1 3.2 比较一下 看 看有没有差别 实际上是没有任何差别的 BMP 模式的 EJB 与 CMP 模式的 EJB 的差别不 在于Home Interface 或者是 Remote Interface 而在于 EJB Class 与 EJB Container 的不同

程序清单 3.7

//File Name: GoodsHome.java //Author:fancy

//Date:2001.5

//Note:the Home Interface

package jdbcbean;

import java.rmi.*;

import javax.ejb.*;

import java.util.*;

public interface GoodsHome extends EJBHome {

public GoodsRemote create(String goodsname String goodstype String comment Double price Double priceoff int id) throws RemoteException CreateException;

public GoodsRemote create(int id) throws RemoteException CreateException;

public GoodsRemote findByPrimaryKey(int primaryKey) throws RemoteException FinderException;



第一部分 JSP 技术与 J2EE 技术

public Collection findAll() throws RemoteException FinderException;

}

程序清单 3.8

//File Name: GoodsRemote.java //Author:fancy

//Date:2001.5

//Note:the Remote Interface

package jdbcbean;

import java.rmi.*;

import javax.ejb.*;

public interface GoodsRemote extends EJBObject {

String getGoodsname() throws RemoteException;

void setGoodsname(String goodsname) throws RemoteException;

String getGoodstype() throws RemoteException;

void setGoodstype(String goodstype) throws RemoteException;

String getComment() throws RemoteException;

void setComment(String comment) throws RemoteException;

Double getPrice() throws RemoteException;

void setPrice(Double price) throws RemoteException;

Double getPriceoff() throws RemoteException;

void setPriceoff(Double priceoff) throws RemoteException;

int getId() throws RemoteException;

}

正因为程序清单3.7 和 3.1 没有差别 程序清单 3.8 和程序清单 3.2 也没有差别 所以 我们就不再重复解释这两个程序了 读者可以参考上文的叙述

3.2.4 EJB 类

在上文我们已经不止一次提到过 BMP 模式的 EJB 与 CMP 模式的 EJB 的结构最大的 不同之处在于EJB Class 与 EJB Container BMP 模式的 EJB 的 EJB Class 分为两个部分 一个是普通的EJB Class(GoodsBean.java) 继承自 EntityBean 接口 这部分只是简单地声明 了 ejbLoad() ejbStore() ejbRemove() ejbActivate() ejbPassivate() ejbCreate()等方法 并没有真正地去实现它们 请看程序清单 3.9(GoodsBean.java) 这一部分和 CMP 模式的 EJB 的 EJB Class 一模一样 读者如果不信 可以比较程序清单 3.9 和程序清单 3.3 的异同 怎么样 找到了吗?实际上 这两个程序没有任何不同 只不过 CMP 模式的 EJB 把这些方 法的实现放到了EJB Container 而在 BMP 模式的 EJB 中 我们必须新定义一个 EJB Class 继承 GoodsBeans 类 并且编码实现 ejbLoad() ejbStore() ejbRemove() ejbActivate() ejbPassivate() ejbCreate()等方法 读者请看程序清单 3.10 (GoodsBeanBMP.java)



public class GoodsBean implements EntityBean {

EntityContext entityContext;

public String goodsname;

public int ejbCreate(String goodsname String goodstype String comment Double price Double priceoff int id) throws CreateException

{

public int ejbCreate(int id) throws CreateException {

return ejbCreate(null null null null null id);

}

public void ejbPostCreate(String goodsname String goodstype String comment Double price Double priceoff int id) throws CreateException

{ }

public void ejbPostCreate(int id) throws CreateException {

ejbPostCreate(null null null null null id);

}

public void ejbRemove() throws RemoveException



public void setEntityContext(EntityContext entityContext) {

this.entityContext = entityContext;

}

public void unsetEntityContext() {

entityContext = null;

}

public String getGoodsname() {

return goodsname;

}

public void setGoodsname(String goodsname) {

this.goodsname = goodsname;

}

public String getGoodstype() {

return goodstype;

}

public void setGoodstype(String goodstype) {

public void setComment(String comment)



第 3 章 EJB 技术进阶

{

this.comment = comment;

}

public Double getPrice() {

return price;

}

public void setPrice(Double price) {

this.price = price;

}

public Double getPriceoff() {

return priceoff;

}

public void setPriceoff(Double priceoff) {

this.priceoff = priceoff;

}

public int getId() {

return id;

} }

程序清单 3.10

//File Name:GoodsBeanBMP.java //Author:Fancy

//Date:2001.5 //Note:the EJB Class

package jdbcbean;

import java.sql.*;

import javax.ejb.*;

import javax.sql.DataSource;

import java.util.*;

public class GoodsBeanBMP extends GoodsBean {

DataSource dataSource;

public int ejbCreate(String goodsname String goodstype String comment Double price Double priceoff int id) throws CreateException

{



第一部分 JSP 技术与 J2EE 技术

super.ejbCreate(goodsname goodstype comment price priceoff id);

try {

//First see if the object already exists ejbFindByPrimaryKey(id);

//If so then we have to throw an exception

throw new DuplicateKeyException("Primary key already exists");

}

statement = connection.prepareStatement("INSERT INTO goods (goodsname goodstype comment price priceoff id) VALUES (? ? ? ? ? ?)");

statement.setString(1 goodsname); (goodsname goodstype comment price priceoff id) VALUES

(? ? ? ? ? ?): " + e.toString());

} finally {



public int ejbCreate(int id) throws CreateException {

return ejbCreate(null null null null null id);

}

public void ejbRemove() throws RemoveException {

statement = connection.prepareStatement("DELETE FROM goods WHERE id = ?");



id = ((Integer) entityContext.getPrimaryKey()).intValue();

Connection connection = null;

PreparedStatement statement = null;

try {

connection = dataSource.getConnection();

statement = connection.prepareStatement("SELECT goodsname goodstype comment price priceoff FROM goods WHERE id = ?");

statement.setInt(1 id);



goodstype comment price priceoff FROM goods WHERE id = ?: "

+e.toString());



statement = connection.prepareStatement("UPDATE goods SET goodsname

= ? goodstype = ? comment = ? price = ? priceoff = ?



public int ejbFindByPrimaryKey(int key) throws ObjectNotFoundException {

statement = connection.prepareStatement("SELECT id FROM goods WHERE id = ?");

statement.setInt(1 key);

ResultSet resultSet = statement.executeQuery();

if (!resultSet.next()) {

throw new ObjectNotFoundException("Primary key does not exist");

}



public Collection ejbFindAll() {

statement = connection.prepareStatement("SELECT id FROM goods");

ResultSet resultSet = statement.executeQuery();



public void setEntityContext(EntityContext entityContext) {

super.setEntityContext(entityContext);

try {

javax.naming.Context context = new javax.naming.InitialContext();

dataSource = (DataSource)



清单3.9 中定义的那些 ejbLoad() ejbStore() ejbRemove() ejbActivate() ejbPassivate() ejbCreate()方法 其实 这些方法的实现代码十分类似 都是建立数据库连接 创建 SQL 句柄对象 然后根据方法的输入参数 设定SQL 语句的输入参数 执行 SQL 语句 把结 果返回

我们就以ejbCreate()方法为例 说明如何做到 BMP 其他方法的实现都和 ejbCreate() 方法的实现差不多 我们就不再介绍了 读者可以模拟得知 ejbCreate()方法的实现代码如 下例所示

public int ejbCreate(String goodsname String goodstype String comment Double price Double priceoff int id) throws CreateException

{

super.ejbCreate(goodsname goodstype comment price priceoff id);

try {

//First see if the object already exists ejbFindByPrimaryKey(id);

//If so then we have to throw an exception

throw new DuplicateKeyException("Primary key already exists");

}

catch(ObjectNotFoundException e) {

//Otherwise we can go ahead and create it...

}

Connection connection = null;

PreparedStatement statement = null;

try {

connection = dataSource.getConnection();

statement = connection.prepareStatement("INSERT INTO goods (goodsname goodstype comment price priceoff id) VALUES (? ? ? ? ? ?)");

statement.setString(1 goodsname);

statement.setString(2 goodstype);

statement.setString(3 comment);

statement.setDouble(4 price.doubleValue());

statement.setDouble(5 priceoff.doubleValue());

statement.setInt(6 id);

if (statement.executeUpdate() != 1)



(goodsname goodstype comment price priceoff id) VALUES (? ? ? ? ? ?): " + e.toString());

super.ejbCreate(goodsname goodstype comment price priceoff id);

判断有没有同样主键存在 如果有 那么会抛出DuplicateKeyException 异常



第一部分 JSP 技术与 J2EE 技术

获取数据库连接对象 如

connection = dataSource.getConnection();

建立SQL 句柄对象 如

statement = connection.prepareStatement("INSERT INTO goods (goodsname goodstype comment price priceoff id) VALUES (? ? ? ? ? ?)");

利用PreparedStatement 对象的 setXXX()方法和输入参数 组装 SQL 语句 如 statement.setString(1 goodsname);

statement.setString(2 goodstype);

调用PreparedStatement 对象的 executeUpdate()方法 执行 SQL 语句 如 if (statement.executeUpdate() != 1)

{

throw new CreateException("Error adding row");

}

关闭statement connection 等对象 如 statement.close();

statement = null;

connection.close();

connection = null;

返回新的EJB 对象的主键(标识符) return id;

这就是 ejbCreate()方法的执行流程 剩余的代码都是错误处理代码 读者不难发现 ejbCreate()方法就是往数据库里插入新的纪录 Entity EJB 就代表一个数据库行纪录 Goods EJB 对象其实就代表了 goods 表的一个行纪录

3.2.5 部署描述符

程序清单3.11 和 3.12 就是 BMP 模式的 Goods EJB 的部署描述符 BMP 模式的 Entity EJB 的部署描述符与 CMP 模式的 Entity EJB 的部署描述符几乎一模一样 不过还是有一点 点小区别 读者不妨比较程序清单 3.11 和程序清单 3.4 看看这两个部署描述符都有哪些 差别

程序清单 3.11(ejb-jar.xml)

<?xml version="1.0" encoding="GBK"?>

<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems Inc.//DTD Enterprise JavaBeans 1.1//EN' 'http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd'>

<ejb-jar>

<enterprise-beans>

<entity>

<ejb-name>Goods</ejb-name>



第 3 章 EJB 技术进阶

<home>jdbcbean.GoodsHome</home>

<remote>jdbcbean.GoodsRemote</remote>

<ejb-class>jdbcbean.GoodsBeanBMP</ejb-class>

<persistence-type>Bean</persistence-type>

<prim-key-class>int</prim-key-class>

<reentrant>False</reentrant>

<resource-ref>

<res-ref-name>jdbc/DataSource</res-ref-name>

<res-type>javax.sql.DataSource</res-type>

<res-auth>Container</res-auth>

</resource-ref>

<ejb-name>Goods</ejb-name>

<method-name>*</method-name>

</method>

<trans-attribute>Required</trans-attribute>

</container-transaction>

下面是程序清单 3.12(ejb-inpries.xml) 它和程序清单 3.5 类似 读者不妨也比较一下 这两者的差别 我们就不再多说了

程序清单 3.12(ejb-inprise.xml)

<?xml version="1.0" encoding="GBK"?>

<!DOCTYPE inprise-specific PUBLIC '-//Inprise Corporation//DTD Enterprise JavaBeans 1.1//EN' 'http://www.borland.com/devsupport/appserver/dtds/ejb-inprise.dtd'>

<inprise-specific>

<enterprise-beans>

<entity>

<ejb-name>Goods</ejb-name>



第一部分 JSP 技术与 J2EE 技术

<bean-home-name>Goods</bean-home-name>

<resource-ref>

<res-ref-name>jdbc/DataSource</res-ref-name>

<jndi-name>DataSource</jndi-name>

</resource-ref>

</entity>

</enterprise-beans>

<datasource-definitions>

<datasource>

<jndi-name>DataSource</jndi-name>

<url>jdbc:odbc:test</url>

<username>sa</username>

<password></password>

<driver-class-name>sun.jdbc.odbc.JdbcOdbcDriver</driver-class-name>

</datasource>

</datasource-definitions>

</inprise-specific>

3.2.6 创建 EJB Container

这一节中 我们需要创建EJB Container 步骤和 3.1.6 节所介绍的步骤一模一样 但 是结果却很不一样 3.1.6 节中创建的 EJB Container 必须实现 ejbLoad() ejbRemove()等方 法 必须管理EJB 和 JDBC 数剧源之间的交互操作 但是在这一节中创建的 EJB Container 却不需要实现这些功能 因为我们已经在程序清单3.10 中实现了 BMP 模式的 EJB Class 这些任务都在其中完成了

3.2.7 创建 EJB 客户端

本小节 我们将编写BMP 模式的 Goods EJB 的客户端程序 与 CMP 模式的 Goods EJB 的客户端程序一样 我们只介绍如何编写基于Java Application 的 EJB 客户端程序 至于基 于JSP 的 EJB 客户端程序 读者可以自己尝试

下面请看程序清单3.13(GoodsTestClient.java) 其中黑体的部分是我们自己加上去的代 码

程序清单 3.13

//File Name:GoodsTestClient.java //Author:fancy

//Date:2001.5.10 //Note:to test the ejb

package jdbcbean;

import javax.naming.*;

import javax.rmi.PortableRemoteObject;



第 3 章 EJB 技术进阶

import java.util.Collection;

import java.util.Iterator;

public class GoodsTestClient {

private static final int MAX_OUTPUT_LINE_LENGTH = 50;

private GoodsHome goodsHome = null;

/**Construct the EJB test client*/

public GoodsTestClient() {

try {

//get naming context

Context ctx = new InitialContext();

//look up jndi name

Object ref = ctx.lookup("Goods");

//cast to Home interface goodsHome = (GoodsHome)

PortableRemoteObject.narrow(ref GoodsHome.class);

GoodsRemote gr=goodsHome.findByPrimaryKey(22);

System.out.println("name:"+gr.getGoodsname());

System.out.println("type:"+gr.getGoodstype());

System.out.println("id:"+gr.getId());

System.out.println("price:"+gr.getPrice());

System.out.println("off:"+gr.getPriceoff());

System.out.println("comment:"+gr.getComment());

System.out.println("is session ejb:"

+goodsHome.getEJBMetaData().isSession());

}

catch(Exception e) {

e.printStackTrace();

} }

//--- // Utility Methods

//---

public GoodsHome getHome() {

return goodsHome;



第一部分 JSP 技术与 J2EE 技术

}

public void executeFindAll() {

if (message.length() > MAX_OUTPUT_LINE_LENGTH) {

System.out.println("-- "

+ message.substring(0 MAX_OUTPUT_LINE_LENGTH) + " ...");

}

GoodsTestClient client = new GoodsTestClient();

//client. executeFindAll();

// Use the getHome() method of the client object to call Home interface // methods that will return a Remote interface reference. Then // use that Remote interface reference to access the EJB.

// For quick access to all available records you can use the



第 3 章 EJB 技术进阶

// executeFindAll() method in this class.

} }

不知道读者发现没有 程序清单3.13 和程序清单 3.6 其实是一模一样的 这说明了一 个问题 那就是CMP EJB 的客户端与 BMP EJB 的客户端程序并没有实质上的区别 尽管 服务端的实现方法可能不一样 但是所对应的客户端程序却是一样的 这也是分布式处理 的好处之一 服务端可以改变服务实现的方法 但是只要提供服务的接口没有变化 客户 端的程序就不需要变化

3.2.8 运行和测试

3.2.8 运行和测试