转贴自 http://hi.baidu.com/zdtools/blog/item/714110a842ccb7b2cb130c53.html
7.6 在RAD中创建CMP Entity Bean
通过CMP来进行数据的持久化操作,这样开发人员就不需要在程序中写与数据库操作有关的SQL了。下面介绍如何在RAD中创建CMP Entity Bean。
7.6.1 在RAD中创建CMP Entity Bean
要实现对数据库的调用,则需要对每个表创建相应的CMP Entity Bean,其创建过程如下。
选择【EJB Projects】→【ejbProject】→【New】→【Other】命令,弹出“Create an enterprise bean”的EJB创建对话框,在对话框的“EJB”目录下选择“Enterprise Bean”,如图7-11所示。
选择“EJB”目录下的“Enterprise Bean”选项,单击【Next】按钮,弹出创建EJB的对话框,如图7-12所示。需要输入创建EJB的如下相关信息。
l Select the EJB type:选“Entity bean with container-managed persistence(CMP)fields”,表示这里将用CMP。
l EJB project:输入EJB Project的名字,这里就是上面刚建立的“ejbProject”,所有的EJB Project都是在这个EJB Project下面工作。以后布置到Enterprise Applications下面的EJB包也是以这个EJB Project的名字来命名的。如本例的EJB包的名字将为“ejbProject.jar”。
l Source folder:远程序的目录。
l Default package:默认的pacakge。
l CMP Version:选2.x,2.x的版本支持本地接口和本地Home接口,这样性能比较好。
图7-11 选择Enterprise Bean
图7-12 创建EJB的对话框
7.6.2 创建CMP Entity Bean的字段
在创建完成上面的CMP的基本信息后,需要进一步加入CMP对应的数据库表的字段,其创建过程如下。
单击图7-12的【Next】按钮,转入EJB详细信息对话框,如图7-13所示。
图7-13 加入CMP的字段
在对话框中选中“Local client view”选项,表示它将只创建本地Home接口(Local home interface)和本地接口(Local interface)。默认会出现一个id字段,根据所要创建的CMP的实际情形进行选择,一般都不会选择这个字段。
还有一个选项是“Use the single key attribute type for the key class”,如果选中该选项,那么CMP将会用这个字段的Java类型作为CMP的主键类,不另外创建CMP主键类。否则,即使是单个主键字段,CMP 将会创建主键类,这只适合单个主键的情形。
对于多个主键字段的情形,CMP一定会创建主键类。
单击【Add】按钮,弹出“CMP Attribute”字段编辑对话框,如图7-14所示。
在对话框中需要输入的CMP的相关字段信息如下。
l Name:输入CMP的字段。
l Type:输入字段的Java类型。
l Key field:确认该字段是否为主键字段。
值得注意的是,主键字段是不能修改的。所以如果选了“Key field”,就不能选下面的Promote方法。
图7-14 加入CMP的主键字段
加入所有字段后,在图7-13所示的“Enterprise Bean Details”界面单击【Finish】按钮就完成了CMP的创建工作。
7.6.3 RAD自动生成的CMP Entity Bean
完成了上面的步骤后,进一步完成对每一个数据库表的CMP的创建工作。本例将会创建对应于数据库表Account、数据库表SequenceNum、数据库表User所对应的3个CMP,如图7-15所示。
图7-15 在RAD中所自动创建的CMP
下面是所创建的CMP实现类AccountBean.java的代码。
package com.sample.cmp.account;
/**
* 实现EntityBean接口类
* Bean implementation class for Enterprise Bean: Account
*/
public abstract class AccountBean implements javax.ejb.EntityBean {
private javax.ejb.EntityContext myEntityCtx;
/**
* setEntityContext
*/
public void setEntityContext(javax.ejb.EntityContext ctx) {
myEntityCtx = ctx;
}
/**
* getEntityContext
*/
public javax.ejb.EntityContext getEntityContext() {
return myEntityCtx;
}
/**
* unsetEntityContext
*/
public void unsetEntityContext() {
myEntityCtx = null;
}
/**
* ejbCreate
*/
public com.sample.cmp.account.AccountKey ejbCreate(
java.lang.Integer accountID,
java.lang.Integer userID) throws javax.ejb.CreateException {
setAccountID(accountID);
setUserID(userID);
return null;
}
/**
* ejbPostCreate
*/
public void ejbPostCreate(
java.lang.Integer accountID,
java.lang.Integer userID) throws javax.ejb.CreateException {
}
/**
* ejbActivate
*/
public void ejbActivate() {
}
/**
* ejbLoad
*/
public void ejbLoad() {
}
/**
* ejbPassivate
*/
public void ejbPassivate() {
}
/**
* ejbRemove
*/
public void ejbRemove() throws javax.ejb.RemoveException {
}
/**
* ejbStore
*/
public void ejbStore() {
}
/**
* Get accessor for persistent attribute: accountID
*/
public abstract java.lang.Integer getAccountID();
/**
* Set accessor for persistent attribute: accountID
*/
public abstract void setAccountID(java.lang.Integer newAccountID);
/**
* Get accessor for persistent attribute: userID
*/
public abstract java.lang.Integer getUserID();
/**
* Set accessor for persistent attribute: userID
*/
public abstract void setUserID(java.lang.Integer newUserID);
/**
* Get accessor for persistent attribute: registrationFee
*/
public abstract java.math.BigDecimal getRegistrationFee();
/**
* Set accessor for persistent attribute: registrationFee
*/
public abstract void setRegistrationFee(
java.math.BigDecimal newRegistrationFee);
}
CMP实现类所包含的内容如下。
l myEntityCtx:CMP的Entity Bean的环境变量。
l setEntityContext:设置环境变量。
l getEntityContext:取出环境变量,以便EJB容器能找到此CMP实例。
l unsetEntityContext:删除环境变量,以便释放相应的资源。
l ejbCreate:RAD会自动生成以主键为输入参数的ejbCreate方法,这个自动生成的ejbCreate方法除主键字段外,没有其他字段的插入,所以一般需要创建新的ejbCreate方法。
l ejbPostCreate:保持和ejbCreate的方法一致。
l ejbActivate:在调用CMP前,准备调用CMP Entity Bean的相应资源。
l ejbLoad:查询取出数据时,保持和数据库同步。
l ejbPassivate:在调用CMP后,释放相应的资源。
l ejbRemove:删除CMP实例,本质上是删除数据库表的一条数据。
l ejbStore:修改或者插入数据库表时,保持和数据库表的同步。
l 所有字段的setter和getter方法。
7.6.4 RAD自动生成的CMP主键类
在创建CMP时,如果不选“Use the single key attribute type for the key class”选项,那么不管是单个主键还是多个主键,CMP都会生成相应的如下主键类。
l accountID:主键字段。
l userID:主键字段。
l AccountKey:无参数的构造方法。
l AccountKey (java.lang.Integer accountID, java.lang.Integer userID):一般会用这个构造函数创建主键类的实例,然后用Home接口的findByPrimaryKey方法创建相应的本地接口方法。
l equals:如果两个主键一致,返回true。
l hashCode:返回主键类的hashCode值。
下面是以AccountKey为例的代码。
package com.sample.cmp.account;
/**
* Key class for Entity Bean: Account
*/
public class AccountKey implements java.io.Serializable {
static final long serialVersionUID = 3206093459760846163L;
/**
* Implementation field for persistent attribute: accountID
*/
public java.lang.Integer accountID;
/**
* Implementation field for persistent attribute: userID
*/
public java.lang.Integer userID;
/**
* Creates an empty key for Entity Bean: Account
*/
public AccountKey() {
}
/**
* Creates a key for Entity Bean: Account
*/
public AccountKey(java.lang.Integer accountID,
java.lang.Integer userID) {
this.accountID = accountID;
this.userID = userID;
}
/**
* Returns true if both keys are equal.
*
*/
public boolean equals(java.lang.Object otherKey) {
if (otherKey instanceof com.sample.cmp.account.AccountKey) {
com.sample.cmp.account.AccountKey o =
(com.sample.cmp.account.AccountKey) otherKey;
return ((this.accountID.equals(o.accountID)) &&
(this.userID.equals(o.userID)));
}
return false;
}
/**
* Returns the hash code for the key.
*
*/
public int hashCode() {
return (accountID.hashCode() + userID.hashCode());
}
}
7.7 在RAD中建立CMP与数据库表之间的映射关系
每一个CMP实际上对应一个数据库表,所以需要建立CMP和数据库表之间的映射(Mapping)。CMP的本质就是要通过EJB容器来实现数据库层的操作,所以CMP和数据库表之间的映射是CMP中最核心的环节。
7.7.1 在RAD中创建meet-In-the-Middle的Mapping关系
为了要在CMP和数据库表之间建立映射关系,需要把数据库表结构和数据库字段导入到EJB Project项目之中,其过程如下。
在“EJB Projects”项目下的“ejbProject(项目名)”上单击鼠标右键,在弹出的快捷菜单中选择【EJB to RDB Mapping】→【Generate Map】命令,如图7-16所示。
图7-16 在RAD中选择Generate Map
进入“EJB to RDB Mapping”对话框,如图7-17所示,选择“Create a new backend folder”选项,将产生一个后台的目录,它将用来保存所要导入的数据库结构和字段,以及CMP和数据库表之间的映射关系等。
图7-17 选择“Create a new backend folder”
选择“Create a new backend folder”选项后,RAD转入“Create new EJB/RDB Mapping”对话框,如图7-18所示,创建一个新的EJB和关系数据库之间的映射关系,有3种映射方式可供选择。
l Bottom-Up:通过数据库表直接生成相应的CMP。
l Top-Down:通过已有的Entity Bean生成相应的数据库表。
l Meet-In-the-Middle:在已有的Entity Bean和已有的数据库表中进行Mapping映射,这是实际工作中最复杂的情况,这里将予以介绍。
图7-18 选择“Meet-In-The-Middle”
单击【Next】按钮,转入数据库JDBC连接对话框,如图7-19所示,在对话框中创建一个数据库的JDBC连接,其目的是要通过这个连接将数据库表导入到EJB的项目中来,以便实现通过图形界面的方法直接创建CMP和数据库表之间的映射关系。
图7-19 创建数据库JDBC连接
需要输入的信息如下。
l Connection name:数据库的连接名。
l Database:数据库。
l User ID:数据库用户名。
l Password:数据库用户密码。
l Database vendor type:数据库厂商类别。
l JDBC driver:JDBC的驱动程序。
l Host:数据库服务器IP。
l Port number:数据库服务器的端口号。
l JDBC driver class:JDBC驱动类。
l Class location:类所在的路径。
单击【Next】按钮,转入“Selective Database Import”对话框,如图7-20所示,用来选择所要导入的数据库表。
图7-20 选择数据库表
单击【Next】按钮,进入“Create New EJB/RDB Mapping”对话框,如图7-21所示,在“Select Meet-in-the-Middle Mapping Options”目录下选择CMP和数据库表的如下自动映射方式。
l None:事先不要在CMP和数据库表之间进行映射。
l Match by Name:通过名字进行映射。
l Match By Name, and Type: 通过名字和数据类型进行映射。
本例选None,即不通过RAD进行自动映射,因为下面将要手动来建立CMP和数据库表的映射关系。
图7-21 选择“None”
7.7.2 在CMP和数据库表间建立映射关系
下面将CMP和导入的数据库表建立映射关系。左边是CMP,右边是数据库表,下面以User的CMP为例来说明如何建立映射关系。
通过鼠标将“Enterprise Beans”目录下面的“ejbProject”→“User”(CMP)选项指向“Tables”目录下面的“bookstdb”(数据库)→“USER”(数据库表),建立CMP和数据库表之间的映射关系。
通过鼠标将“Enterprise Beans”目录下面的“ejbProject”→“User”(CMP)→“userID”(CMP主键字段)选项指向“Tables”目录下面的 “bookstdb”(数据库)→“USER”(数据库表)→“USERID”(数据库表主键字段),建立CMP主键字段和数据库表主键字段之间的映射关 系。
同理,可以建立CMP的其他字段和数据库表其他字段的映射关系。所建立的User(CMP)和数据库表的映射如图7-22所示。
图7-22 建立User(CMP)和数据库表的映射关系
7.8 创建新的CMP的ejbCreate方法
在RAD创建CMP时,会自动生成以主键为输入参数的ejbCreate方法,这个自动生成的ejbCreate方法只有主键字段的插入,没有其他字段的插入,所以一般需要创建新ejbCreate方法。
此外,在创建ejbCreate方法的时候,在数据 库表字段比较多的情况下,如果把所有的字段都作为create方法的输入参数的话,一方面,这样的ejbCreate方法会很长,不容易读;另一方面,如 果以后数据库表的字段出现增加和删除的情况,那么ejbCreate方法需要修改,因而笔者建议将创建输入参数的DTO数据类作为ejbCreate方法 的输入参数,这样易于程序的开发和维护,事实上,这也是在实际项目中经常用到的。
7.8.1 创建新的CMP的ejbCreate方法
ejbCreate方法主要目的是将数据插入数据库表,只要定义好了输入参数,然后调用setter方法将输入参数一一加入CMP中即可,参见如下代码。
package com.sample.cmp.user;
import com.sample.model.service.dto.UserAccountDTO;
/**
* Bean implementation class for Enterprise Bean: User
*/
public abstract class UserBean implements javax.ejb.EntityBean {
/**
* ejbCreate主要将用户数据插入User数据库表
*/
public java.lang.Integer ejbCreate(Integer userID,
UserAccountDTO userAccountDTO) throws
javax.ejb.CreateException {
// EJB 2.0 spec says return null for CMP ejbCreate methods.
//设置所有Entity Bean CMP的字段
setUserID(userID);
setLoginName(userAccountDTO.getLoginName());
setName(userAccountDTO.getName());
setPassword(userAccountDTO.getPassword());
setEmail(userAccountDTO.getEmail());
setPhone(userAccountDTO.getPhone());
return null; //返回null 是必须的
// end-user-code
}
/**
* 和ejbCreate方法的接口一致
* @param userID
* @param userAccountDTO
* @throws javax.ejb.CreateException
*/
public void ejbPostCreate(Integer userID,
UserAccountDTO userAccountDTO) throws
javax.ejb.CreateException {
// begin-user-code
// end-user-code
}
}
7.8.2 创建ejbCreate方法输入参数的DTO类
上面已经在ejbCreate方法中加入了输入参数 UserAccountDTO类,这里将创建UserAccountDTO类,如图7-23所示。UserAccountDTO用来存储和用户User有 关的信息。单击ejbCreate方法中的userAccountDTO,输入相应的UserAccountDTO类的信息,就创建了一个空的 UserAccountDTO类,接着加入相应的字段和相应的setter和getter方法即可。
当然,开发人员也可以在RAD上先创建DTO类,再将它作为输入参数加入ejbCreate方法。
图7-23 创建ejbCreate方法的输入参数DTO类
7.8.3 将ejbCreate方法提升到本地Home接口
在CMP中创建了ejbCreate方法后,需要将 其提升到本地Home接口以被调用,如图7-24所示,在“ejbCreate”上单击鼠标右键,在弹出的快捷菜单中选择【Enterprise Bean】→【Promote to Local Home Interface】命令。
图7-24 将ejbCreate方法提升到本地Home接口
在相应的本地Home接口中将生成新的create方法,UserLocalHome.java参见如下代码。
package com.sample.cmp.user;
import com.sample.model.service.dto.UserAccountDTO;
/**
* Local Home interface for Enterprise Bean: User
*/
public interface UserLocalHome extends javax.ejb.EJBLocalHome {
/**
* RAD自动生成的以主键为输入参数的create方法
* Creates an instance from a key for Entity Bean: User
*/
public com.sample.cmp.user.UserLocal create(java.lang.Integer userID)
throws javax.ejb.CreateException;
/**
* RAD自动生成的findByPrimaryKey方法,通过主键查询返回本地接口
* Finds an instance using a key for Entity Bean: User
*/
public com.sample.cmp.user.UserLocal findByPrimaryKey(
com.sample.cmp.user.UserKey primaryKey)
throws javax.ejb.FinderException;
/**
* 通过对将ejbCreate方法提升到本地Home接口所生成的create方法
* @param userID
* @param userAccountDTO
* @return
* @throws javax.ejb.CreateException
*/
public com.sample.cmp.user.UserLocal create(
Integer userID,
UserAccountDTO userAccountDTO) throws
javax.ejb.CreateException;
}
7.9 创建新的CMP的finder方法
RAD会自动创建findByPrimaryKey 的方法,它将根据主键查询返回一条数据库表的记录。在实际项目中经常遇到根据某些条件进行数据查询的业务需求,其实现本质即是通过某些参数找到对应于数据 库表的多条相应的数据,以便输出报表显示,或者修改,或者删除。所以需要创建新的CMP的finder方法。创建新的CMP的finder方法过程如下。
(1)在CMP中加入新的finder,如图7-25所示,在“EJB Deployment Descriptor”对话框中单击“Queries”目录下面的【Add】按钮。
图7-25 在CMP中加入新的finder
(2)进入finder方法创建对话框,如图7-26所示。
图7-26 输入相应的finder方法内容
(3)输入相应的finder方法的名字和参数如图7-27所示,在文本框“Name”中输入finder方 法名“findByUserID”;单击【Add】按钮,在弹出的“Add Method Parameter”对话框中的“Name”文本框中输入参数名称“userID”,在“Type”下拉列表中选择参数类型 “java.lang.String”,单击【OK】按钮将相应的参数加入到finder方法中。
图7-27 输入相应的finder方法的名字和参数
(4)输入相应的finder方法的返回类型,如图7-28所示,在“Return type”下拉列表中选择相应的类型选项,如“java.util.Collection”,表示将返回一个本地接口对象的集合,单击【Next】按钮。
图7-28 选择相应的finder方法的返回类型
(5)在“Query statement”目录下输入相应的SQL,注意这个SQL是基于CMP的对象名和字段名的,而不是基于数据库的表名和字段名的,单击【Finish】按钮,如图7-29所示。
图7-29 输入相应的SQL
一旦完成了上述过程,相应的Home Interface中也会生成相应的接口以被调用,如下面代码所示。
package com.sample.cmp.user;
/**
* Local Home interface for Enterprise Bean: User
*/
public interface UserLocalHome extends javax.ejb.EJBLocalHome {
/**
* RAD自动生成的以主键为输入参数的create方法
* Creates an instance from a key for Entity Bean: User
*/
public com.sample.cmp.user.UserLocal
create(java.lang.Integer userID)
throws javax.ejb.CreateException;
/**
* RAD自动生成的findByPrimaryKey方法,通过主键查询,返回本地接口
* Finds an instance using a key for Entity Bean: User
*/
public com.sample.cmp.user.UserLocal findByPrimaryKey(
com.sample.cmp.user.UserKey primaryKey)
throws javax.ejb.FinderException;
/**
* 新生成的findByLoginName,通过输入参数loginName,返回相应的数据库表的记录
*/
public java.util.Collection findByLoginName(
java.lang.String loginName) throws
javax.ejb.FinderException;
}
7.12 在RAD中加入应用服务器Server
EJB容器一定需要应用服务器运行,在RAD上所开 发的Web应用和EJB应用也需要发布到应用服务器上,因而需要在RAD中加入服务器(Server),实现RAD和应用服务器的集成。RAD上自带 Websphere的各种版本服务器,本章的例子将加入Websphere 6.0服务器。下面显示了具体过程。
同创建CMP的过程一样,选择“EJB Projects”→“ejbProject”→“New”→“Other”选项,弹出“Server”列表,如图7-38所示。
图7-38 选择服务器Server
选择“Server”目录下面的“Server”选项,单击【Next】按钮,弹出“New Server”编辑对话框,如图7-39所示,所要输入的Server信息如下。
图7-39 选择Websphere v6.0 Server
(1)Host name:选择下拉列表中的应用服务器的IP地址。
(2)Select the server type:RAD提供了如下4种Server类型可供选择。
l Websphere v5 Server Attach
l Websphere v5.0 Server
l Websphere v5.1 Server
l Websphere v6.0 Server
本例将选择Websphere v6.0 Server,因为下面章节的IBM服务总线产品SIBus只有Websphere v6.0 Server才提供。
单击【Next】按钮,就可以完成对服务器的设置。
7.12.1 将Enterprise Application Project加入Server
J2EE项目需要布置到服务器上才能运行。所有的 Web应用项目、EJB应用项目和Web Service项目都会加入到RAD的Enterprise Applications项目中。然后将Enterprise Applications项目加入到服务器中去,如图7-40所示,在左边列表中选中所要发布到服务器的Enterprise Applications项目,如本例的“ejbProjectEAR”,然后单击【Add】按钮,这样可以将所选中的ear项目加入到应用服务器中。最 后单击【Finish】按钮即可完成加入过程。
图7-40 将Enterprise Project加入到Server
7.12.2 运行服务器 Server和打开Admin Console
将EJB项目加入服务器后,就可以运行服务器,过程如图7-41所示,在RAD右下框,单击加入的应用服务器,在弹出的快捷菜单中选择【Start】命令即可。
图7-41 运行服务器Server
运行服务器后,可以通过Websphere提供的 Administrative Console来对服务器进行配置和监控。在应用服务器完全启动后,单击应用服务器,在弹出的快捷菜单中选择【Run administrative console】命令,如图7-42所示。
图7-42 运行Admin Console
在弹出的“欢迎,请输入您的信息”界面的“用户标识”文本框中输入任意一个名字,不需要输入密码,单击【登录】按钮即可进入Admin Console界面,如图7-43所示。
图7-43 登录Admin Console
7.13 创建数据源和在CMP中配置数据源
Entity Bean CMP对数据库的调用是通过数据源来实现的,CMP只需要通过JNDI调用相应的数据源即可,由数据源指向数据库实现JDBC的连接。
7.13.1 创建安全机制
Websphere有一套特殊的方法来处理用户名和 密码,那就是安全机制,它将用户名和密码存入到一套安全机制对象中。需要输入用户名和密码的地方,如建立数据库连接,直接选择相应的安全机制即可,而不是 将用户名和密码硬写在数据库的连接中。这样,如果以后的用户名和密码变化的话,只需要对安全机制进行修改即可,而不需要直接去修改数据库连接。所以如果创 建数据源,首先需要创建数据库的用户名和密码的安全机制。下面是创建过程。
在“Admin Console”左边列表中选择“安全性”→“全局安全性”选项,如图7-44所示。
图7-44 选择“全局安全性”
在右边列表中的“认证”目录下面选择“JAAS 配置”→“J2C 认证数据”选项,弹出“J2EE连接器体系结构(J2C)认证数据条目”对话框,如图7-45所示。
图7-45 “J2EE连接器体系结构(J2C)认证数据条目”对话框
单击【新建】按钮,在弹出的如图7-46所示的对话框中输入相应的用户名和密码信息。
l 别名:可以任意取一个名字。
l 用户标识:这里输入数据库用户名。
l 密码:输入数据库用户的密码。
l 描述:可以输入任意的特定描述,以区别于其他的用户名和密码。
图7-46 创建J2C认证数据
单击【应用】按钮,就创建了相应的J2C认证数据,如图7-47所示。
图7-47 所创建的J2C认证数据
7.13.2 创建JDBC
JDBC是Java访问数据库的接口,它通过驱动程序(Driver)的方式来实现,保证了Java程序对不同类型数据库调用时用的是同一接口,创建JDBC的过程如下:
在“Admin Console”界面中的左边列表中选择“资源”→“JDBC提供者”选项,打开“JDBC提供者”对话框,如图7-48所示。
单击【新建】按钮,弹出“JDBC提供者>新建”对话框,如图7-49所示。选择下列参数。
l 选择数据库类型:本例用DB2,也可以使用其他的数据库,关键是要有JDBC的支持。
l 选择提供者类型:对应于DB2,本例选择“DB2 Universal JDBC Driver Provider”选项。
l 选择实现类型:选择“连接池数据源”选项。
图7-48 “JDBC提供者”对话框
图7-49 选择数据库类型
单击【下一步】按钮,弹出JDBC的Driver编辑对话框,如图7-50所示。上面的数据库不管是不是 DB2,关键是要在下面建立JDBC的Driver的类路径classpath。这样,在Websphere服务器运行时,就会找到相应的Driver的 classpath,建立正确的数据库连接,否则,是无法连接到数据库的。因为本实例是以DB2作为数据库的,所以实例路径是DB2的Driver的类路 径classpath。
图7-50 自动生成的JDBC路径
7.13.3 创建JDBC驱动程序Driver的路径变量
实例的类路径是Websphere服务器为DB2数据库的JDBC Driver所创建的类路径,其中的环境变量需要另外单独定义。只有定义这些环境变量后,Websphere应用服务器才能找到相应的JDBC驱动程序 Driver。图7-50所对应的如下的类路径需要定义:
${DB2UNIVERSAL_JDBC_DRIVER_PATH}/db2jcc_license_cisuz.jar
下面将介绍如何创建环境变量“DB2UNIVERSAL_JDBC_DRIVER_PATH”来指向正确的JDBC Driver的类路径。
在“Admin Console”界面中的左边列表中选择“环境”→“Websphere变量”选项,显示界面如图7-51所示。
图7-51 创建路径(path)变量
单击“名称”目录下面的“DB2UNIVERSAL_JDBC_DRIVER_PATH”选项,在弹出的对话框中输入变量名和路径,如图7-52所示。
l 名称:所要编辑的变量名。
l 值:输入相应路径,本例为“E:/Program Files/IBM/SQLLIB/java”,对应于笔者机器DB2的安装路径,开发人员需要根据自己所装载的DB2的路径来定义。
图7-52 创建DB2UNIVERSAL_JDBC_DRIVER_PATH变量
单击【应用】按钮即可创建相应的环境变量。
7.13.4 创建数据源
Websphere通过创建数据源来指向相应的数据库地址,创建数据源的过程如下。
打开上面的所创建的JDBC的链接,弹出所创建的“JDBC提供者”对话框,如图7-53所示。
图7-53 “JDBC提供者”对话框
在“其他属性”目录下,单击“数据源”选项,进入“数据源”对话框,如图7-54所示。注意,如果应用程序使用 EJB1.1或者Servlet 2.2,则必须使用“数据源(V4)”。
图7-54 新建数据源
单击【新建】按钮,进入创建数据源界面,如图7-55所示,创建数据源时需要输入下面信息。
图7-55 输入创建JDBC所需数据库信息
l 作用域:和服务器名称有关,开发人员不能修改。
l 名称:定义一个数据源的名字。
l JNDI 名称:输入一个唯一的JNDI的名字,如“jdbc/bkstore”,同时选取“将此数据源用于容器管理持久性(CMP)”选项,CMP将会用这个JNDI名建立数据库连接。
l 描述:输入一些描述该数据源的文字。
l 选择数据存储helper类:选取该选项,它将扩展JDBC驱动程序实现类的能力以执行数据库特定的功能;选中“DB2通用数据存储helper”。
l 组件管理的认证别名:选择有关用户名和密码的安全机制。
l 数据库名称:选择所创建的数据库。
l 驱动程序类型:默认4。
l 服务器名:数据库的服务器地址。
l 端口号:数据库服务器的端口号。
单击【应用】按钮,将会创建相应的数据源。注意,在“Admin Console”中完成任何修改后,一定要选择系统提示的“保存”选项。
7.13.5 测试数据源连接
上面的步骤已经完成了所有的数据源配置工作,下面介 绍如何检查配置是否正确,包括JAAS的安全机制、JDBC、JDBC驱动Driver的路径变量、数据库名、地址和端口等的配置检查,这些检查可以通过 测试数据源的连接来完成,直接单击【测试连接】按钮即可。本例可以看到连接成功的信息输出,如图7-56所示。
图7-56 测试数据源连接
7.13.6 配置CMP指向数据源的JNDI名
上面已经完成了CMP的创建和数据源的创建,现在进一步配置将CMP指向数据源的JNDI名,就可以完成CMP和数据库之间的关联,这是CMP连接数据库的关键。在RAD中的配置过程如下。
在RAD的“Project Explorer”对话框中选择【EJB Projects】→【ejbProject】→“Deployment Descriptor:ejbProject”选项;在弹出的对话框的“JNDI name”文本框中输入数据源的JNDI名称即可,本例为jdbc/bkstore,如图7-57所示。
图7-57 在CMP中建立数据库的连接
7.14 在RAD上创建基于Session Bean的Web Service
EJB2.1规定了无状态会话 Bean(Stateless Session Bean)对Web Service的支持,所以现在可以直接用各个厂商的IDE工具将无状态会话Bean发布为Web Service,实现远程调用。本节将介绍如何用RAD将Stateless Session Bean发布为Web Service,开发人员就不需要再编写Web Service的服务器端程序了。
在服务器端实现Web Service有两种方法:一种是直接通过Java Bean来创建Web Service;另一种是通过无状态会话Bean创建Web Service。因为无状态会话Bean有事物管理等功能,另外,为了保持本书的连贯性,这里介绍创建基于Session Bean的Web Service方法(在RAD上创建基于Java Bean的Web Service见本书附录C)。因为第5章已经在RAD上创建了Session Bean和CMP,这里进一步将第5章的Session Bean发布成Web Service,包括两个基本步骤。
(1)启动应用服务器。
(2)创建Web Service。
在RAD上发布Web Service时,首先需要启动应用服务器,否则在发布Web Service的过程中将会出现错误。启动服务器后,下面介绍在RAD上创建基于Session Bean的Web Service的具体过程。
1.创建Web Service
在RAD上创建基于Session Bean的Web Service不需要写任何程序,所有的过程都通过RAD来创建,其过程如下。
在RAD主界面选择【File】→【New】→【Other】命令,弹出“Select a wizard”对话框如图7-58所示。
图7-58 “Select a wizard”对话框
选中“Web Service”选项,单击【Next】按钮,转入“Web Service”对话框,如图7-59所示。在对话框的“Web service type”下拉列表中选择“EJB Web Service”选项,表示将会通过无状态会话Bean来创建Web Service。其他的选项采用默认设置即可。
图7-59 “Web Service”对话框
单击【Next】按钮,RAD将会自动从EJB Project中找到相应的无状态会话Bean,本例将会显示无状态会话Bean——UserAccountMgr,如图7-60所示。
图7-60 选择EJB Session Bean
单击【Next】按钮,转入“Service Deployment Configuration”对话框,如图7-61所示,在对话框的“Service project”下拉列表中选择上面创建的EJB项目“ejbProject”;在“EAR project”下拉列表中选择上面创建的EAR项目“ejbProjectEAR”。
图7-61 选择相应的选项
单击【Next】按钮,在弹出对话框的文本框 “Select Router Project”中输入“sampleRouterProject”的名称,决定Web Service的发布路径。选取“Select transports”目录下面的“SOAP over HTTP”选项,表示此Web Service将会采用SOAP和HTTP的传输协议,如图7-62所示。
图7-62 输入sampleRouterProject的名称
单击【Next】按钮,运行一段时间,将会出现包含创建的Web Service各种信息的对话框,选择要发布的Session Bean方法,如图7-63所示。
l Web service URI:Web Service的地址。
l WSDL Folder:WSDL所在目录。
l WSDL File:描述Web Service的文件名wsdl。
l Methods:可以选择将哪些方法发布成Web Service。
单击【Next】按钮,将会进入Web Service的发布对话框,如图7-64所示,主要让用户确认是否想把该Web Service注册到UDDI,以便外界能够查询到。本例不选择发布到UDDI,因为Web Service有它自己的地址。
图7-63 选择所要发布的Session Bean方法
图7-64 Web Service创建成功
单击【Finish】按钮,完成整个Web Service的创建工作。
2.在RAD上测试Web Service的功能
RAD提供了强大的Web Service的测试功能,开发人员不需要写任何程序,就能进行Web Service的测试。在“Project Explorer”对话框中的“Web Services”→“Services”→“UserAccountMgrService”上单击鼠标右键,在弹出的快捷菜单中选择【Test with Web Services Explorer】命令,如图7-65所示。
图7-65 用Web Services Explorer测试Web Service
在弹出对话框的左边列表中选择所要测试的服务接口方 法,如“getUserList”,单击右边列表中的【执行】按钮,从右下框可以看到相应的结果,测试非常方便。此Web Service会自动去调用Session Bean和CMP相应的方法,给出相应的结果,如图7-66所示。
图7-66 调用Web Service的方法
7.15 RAD生成的WSDL
经过上面的过程,将会产生UserAccountMgr.wsdl,如图7-67所示。WSDL文件的内容包括Web Service所提供的服务的传输方式、服务方法接口、接口参数、服务地址和服务绑定等。
图7-67 RAD自动生成的WSDL文件
WSDL的图形关系如图7-68所示。Services定义服务地址URI、Bindings定义传输协议、Port Types定义服务接口和Messages定义服务消息。
图7-68 RAD所显示的WSDL
下面是RAD自动生成的UserAccountMgr.wsdl的源码。 WSDL文档出现的初衷是要让使用者根据这个文档编写相应的SOAP消息来调用Web Service。然而,Axis包已经可以自动根据WSDL生成相应的Java调用程序,所以开发人员不是很需要理解这个文档了。
UserAccountMgr.wsdl的内容如下 面代码所示。开始部分说明此WSDL文件采用 XML 1.0 版本,语言集为UTF-8,并说明了一些WSDL的定义类型;接着用<wsdl:types>定义了此WSDL所包含的元素,如定义了 getUserListResponse的元素nillable为true,表示可以为空,返回数据类型为 ArrayOf_tns2_UserAccountDTO。
l 定义接口方法checkUserLogin,输入参数及类型,以及输入参数是否可以为空。
l 定义传输类参数UserAccountDTO,以及它所包含的字段名、类型、是否可以为空。
l 定义异常类ApplicationException,以及它所包含的字段名、类型、是否可以为空。
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace=http://sessionbean.ejb.sample.com
xmlns:impl="http://sessionbean.ejb.sample.com"
xmlns:intf=http://sessionbean.ejb.sample.com
xmlns:tns2="http://dto.service.model.sample.com"
xmlns:tns3=http://exception.sample.com
xmlns:wsdl="http://schemas.xmlSOAP.org/wsdl/"
xmlns:wsdlSOAP=http://schemas.xmlSOAP.org/wsdl/SOAP/
xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<schema targetNamespace=http://sessionbean.ejb.sample.com
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:impl=http://sessionbean.ejb.sample.com
xmlns:intf="http://sessionbean.ejb.sample.com"
xmlns:tns2=http://dto.service.model.sample.com
xmlns:wsdl="http://schemas.xmlSOAP.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<import namespace="http://dto.service.model.sample.com"/>
<element name="getUserListResponse">
<complexType>
<sequence>
<element name="getUserListReturn" nillable="true"
type="impl:ArrayOf_tns2_UserAccountDTO"/>
</sequence>
</complexType>
</element>
<element name="checkUserLogin">
<complexType>
<sequence>
<element name="loginName" nillable="true" type="xsd:string"/>
<element name="password" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</element>
<schema targetNamespace=http://dto.service.model.sample.com
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:impl=http://sessionbean.ejb.sample.com
xmlns:intf="http://sessionbean.ejb.sample.com"
xmlns:wsdl=http://schemas.xmlSOAP.org/wsdl/
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<complexType name="UserAccountDTO">
<sequence>
<element name="email" nillable="true" type="xsd:string"/>
<element name="loginName" nillable="true" type="xsd:string"/>
<element name="name" nillable="true" type="xsd:string"/>
<element name="password" nillable="true" type="xsd:string"/>
<element name="phone" nillable="true" type="xsd:string"/>
<element name="registrationFee" nillable="true"
type="xsd:decimal"/>
<element name="registrationTime" nillable="true" type="xsd:long"/>
<element name="userAccountID" nillable="true" type="xsd:int"/>
</sequence>
</complexType>
</schema>
<schema targetNamespace="http://exception.sample.com"
xmlns=http://www.w3.org/2001/XMLSchema
xmlns:impl="http://sessionbean.ejb.sample.com"
xmlns:intf=http://sessionbean.ejb.sample.com
xmlns:wsdl="http://schemas.xmlSOAP.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<complexType name="ApplicationException">
<sequence>
<element name="message" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="ApplicationException" nillable="true"
type="tns3:ApplicationException"/>
</schema>
</wsdl:types>
下面定义SOAP的请求消息:getUserListRequest和saveUserInfoRequest等;定义 SOAP的返回消息:getUserListResponse和saveUserInfoResponse;定义异常类消息 ApplicationException。
<wsdl:message name="getUserListRequest">
<wsdl:part element="impl:getUserList" name="parameters"/>
</wsdl:message>
<wsdl:message name="saveUserInfoRequest">
<wsdl:part element="impl:saveUserInfo" name="parameters"/>
</wsdl:message>
<!--定义SOAP的返回消息-->
<wsdl:message name="getUserListResponse">
<wsdl:part element="impl:getUserListResponse" name="parameters"/>
</wsdl:message>
<wsdl:message name="checkUserLoginRequest">
<wsdl:part element="impl:checkUserLogin" name="parameters"/>
</wsdl:message>
<wsdl:message name="saveUserInfoResponse">
<wsdl:part element="impl:saveUserInfoResponse"
name="parameters"/>
</wsdl:message>
<wsdl:message name="checkUserLoginResponse">
<wsdl:part element="impl:checkUserLoginResponse"
name="parameters"/>
</wsdl:message>
<wsdl:message name="ApplicationException">
<wsdl:part name="fault" type="tns3:ApplicationException"/>
</wsdl:message>
下面通过<wsdl:portType>定义服务接口UserAccountMgr,通 过<wsdl:operation>来绑定服务接口方法getUserList和checkUserLogin等,通 过<wsdl:input>来绑定请求消息getUserListRequest和checkUserLoginRequest,通 过<wsdl:output>来绑定返回消息getUserListResponse和checkUserLoginResponse,通 过<wsdl:fault>来绑定异常类消息ApplicationException,如下所示。
<wsdl:portType name="UserAccountMgr">
<wsdl:operation name="getUserList">
<wsdl:input message="impl:getUserListRequest"
name="getUserListRequest"/>
<wsdl:output message="impl:getUserListResponse"
name="getUserListResponse"/>
</wsdl:operation>
<wsdl:operation name="checkUserLogin">
<wsdl:input message="impl:checkUserLoginRequest"
name="checkUserLoginRequest"/>
<wsdl:output message="impl:checkUserLoginResponse"
name="checkUserLoginResponse"/>
<wsdl:fault message="impl:ApplicationException"
name="ApplicationException"/>
</wsdl:operation>
<wsdl:operation name="saveUserInfo">
<wsdl:input message="impl:saveUserInfoRequest"
name="saveUserInfoRequest"/>
<wsdl:output message="impl:saveUserInfoResponse"
name="saveUserInfoResponse"/>
</wsdl:operation>
</wsdl:portType>
下面通过<wsdl:binding>将服务接口和传输协议进行绑定,首先创建一个绑定名,这里为 UserAccountMgrSOAPBinding,通过type属性定义所要绑定的服务接口UserAccountMgr,通过 wsdlSOAP:binding 元素的transport属性定义传输协议。
通过wsdl:service来定义Web Service的发布地址,wsdlSOAP:address元素的location属性定义了Web Service的地址,如下所示。
<wsdl:binding name="UserAccountMgrSOAPBinding"
type="impl:UserAccountMgr">
<wsdlSOAP:binding style="document"
transport="http://schemas.xmlSOAP.org/SOAP/http"/>
<wsdl:operation name="getUserList">
<wsdlSOAP:operation SOAPAction=""/>
<wsdl:input name="getUserListRequest">
<wsdlSOAP:body use="literal"/>
</wsdl:input>
<wsdl:output name="getUserListResponse">
<wsdlSOAP:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="checkUserLogin">
<wsdlSOAP:operation SOAPAction=""/>
<wsdl:input name="checkUserLoginRequest">
<wsdlSOAP:body use="literal"/>
</wsdl:input>
<wsdl:output name="checkUserLoginResponse">
<wsdlSOAP:body use="literal"/>
</wsdl:output>
<wsdl:fault name="ApplicationException">
<wsdlSOAP:fault name="ApplicationException" use="literal"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="saveUserInfo">
<wsdlSOAP:operation SOAPAction=""/>
<wsdl:input name="saveUserInfoRequest">
<wsdlSOAP:body use="literal"/>
</wsdl:input>
<wsdl:output name="saveUserInfoResponse">
<wsdlSOAP:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="UserAccountMgrService">
<wsdl:port binding="impl:UserAccountMgrSOAPBinding"
name="UserAccountMgr">
<wsdlSOAP:address location="http://localhost:9080
/routerProject/services/UserAccountMgr"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>