8.4 创建EJB模块
有了上一节配置的数据源,就可以在工程中创建实现操作login数据库的实体Bean模块,并部署到服务器上,然后登录示例,就可以通过访问这个模块来实现对应的功能。
8.4.1 创建EJB工程
1.创建工程UserEJB
首先创建开发EJB的工程UserEJB,单击【File】|【New Project…】命令菜单,弹出新建工程对话框,输入工程名称和目录,工程创建配置如图8-32所示。

图8-32 创建工程UserEJB
2.配置服务器
然后要配置服务器为Weblogic 8.1,步骤如下。
(1)单击【Project】|【Project Properties…】命令菜单,在工程配置对话框中选择“Server”项,单击其中的【…】按钮配置服务器,如图8-33所示。

图8-33 配置服务器
(2)在服务器列表中选中Weblogic8.1服务器,选中“Enable Server”复选框,如图8-34所示。
图8-34 配置服务器
(3)切换到“Custom”项,在这里配置一系列的属性,如图8-35所示,其中用户名为服务器的用户名“kingbill”,密码为“2222222”。

图8-35 “Custom”项内容
(4)切换到“General”项,如图8-36所示,这时“1”处显示了访问服务器的账户,“2”处被自动设置为正确的配置。另外需要手工导入数据库驱动,具体配置如图8-37所示。

图8-36 “General”项
(5)单击【OK】按钮,这时服务器被选择成刚才配置的Weblogic 8.1,如图8-38所示。

图8-37 服务器为Weblogic8.1 图8-38 “Database Drivers”项内容
3.配置企业级应用数据库驱动
接下来配置企业级应用数据库驱动,步骤如下。
(1)单击【Enterprise】|【Enterprise Setup…】命令菜单,在Enterprise Setup对话框中选择“Database Drivers”项,如图8-38所示。
(2)单击【Add…】按钮,出现选择lib包对话框,选中前面内容中创建的MySQL数据库驱动的lib包,如图8-39所示。
(3)单击【OK】按钮,在“Database Drivers”列表中就增加了一项mysql.config配置,如图8-40所示。

图8-39 选择lib包 图8-40 增加了mysql.config配置
(4)单击【OK】按钮完成配置,系统提示重启Jbuilder 2005才能使配置生效。
8.4.2 创建EJB模块
接下来就可以在该工程中创建EJB模块,步骤如下。
(1)单击【File】|【New…】命令菜单,弹出新建对象向导对话框,选择EJB项中的“EJB Modul”图标,如图8-41所示。
(2)单击【OK】按钮,选择创建一个新的模块,如图8-42所示。

图8-41 新建对象向导 图8-42 创建一个新的模块
(3)单击【Next】按钮,输入模块的名称“user”,选择EJB版本为EJB 2.0,如图8-43所示。
(4)单击【Finish】按钮,工程中就出现了一个名为“user”的EJB模块,如图8-44所示。

图8-43 创建EJB模块user 图8-44 EJB模块白板(面板)
8.4.3 创建实体Bean
1.创建实体Bean
接下来要在该模块中创建对应login数据库中user表的实体Bean(CMP),步骤如下。
(1)在“Structure”面板中右击“DataSources”图标,选择“Import Schema From Database”,如图8-45所示。

图8-45 Import Schema From Database
(2)在弹出的“Database Schema Provider”对话框中,输入数据库驱动类名和数据库URL,以及在前面建立的数据源的名称“UserDataSource”,如图8-46所示,注意上面的“All Schemas”复选框一定要选上,否则将看不到数据库中表的列表信息。

图8-46 Database Schema Provider
(3)完成配置后单击【OK】按钮,在工程的“Structure”面板中就出现了该数据源,展开后可以看到其中有一张表“user”,以及该表的3个字段,如图8-47所示。
(4)在表“user”的图标上右击,在弹出的菜单中选择“Create CMP 2.0 Entity Bean”,来创建对应的实体Bean,如图8-48所示。
图8-47 “Structure”面板出现数据源

图8-48 Create CMP 2.0 Entity Bean
(5)系统创建的实体Bean会以可视化的方式显示在模块的白板中,如图8-49所示。
(6)在实体Bean的名称“User”上单击,会弹出该对象的属性对话框,将其中的“Interfaces”一项修改为“Local/Remote”,如图8-50、图8-51所示。
(7)完成上述操作后,该工程下共有5个类(其实是5个接口),如图8-52所示。
(8)为该实体Bean添加一个按照username查找的方法。在实体Bean的名称“User”上右击,依次选择【Add】|【Finder】项,如图8-53所示。
(9)配置该方法,各项配置如图8-54所示。注意其中输入的查询语句是EJB QL标准的SQL语句“select object(u) from User as u where u.username=?1”,EJB QL语句是对SQL语句标准的一个标准实现,同一般的SQL语句有所区别。

图8-49 实体Bean 图8-50 “属性”对话框
图8-51 修改为“Local/Remote” 图8-52 工程下共有5个类

图8-53 添加Finder方法 图8-54 配置Finder方法
2.实体Bean中的5个接口
完成上述操作后,基本的实体Bean就建立好了。这里简单介绍一下其中生成的5个接口。
本地接口User:实现具体数据库访问逻辑的代理接口,具体数据库访问逻辑则委托给Bean类,再由容器实现,换句话说就是与开发者无关,其代码如下(大致了解即可)。
代码8.1 本地接口
package userejb;
import javax.ejb.EJBLocalObject;
public interface User
extends EJBLocalObject {
public Integer getUserId();
public void setUsername(String username);
public String getUsername();
public void setPassword(String password);
public String getPassword();
}
本地Home接口UserHome:是生产本地接口的工厂,由JNDI查找获得。代码如下。
代码8.2 本地Home接口
package userejb;
import javax.ejb.EJBLocalHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
public interface UserHome
extends EJBLocalHome {
public User create(Integer userId) throws CreateException;
public User findByPrimaryKey(Integer userId) throws FinderException;
public User findByUserName(String username) throws FinderException;
}
远程接口UserRemote:对应本地接口User。区别在于本地接口用于本地访问,远程接口用于远程访问,代码如下。
代码8.3 远程接口
package userejb;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface UserRemote
extends EJBObject {
public Integer getUserId() throws RemoteException;
public void setUsername(String username) throws RemoteException;
public String getUsername() throws RemoteException;
public void setPassword(String password) throws RemoteException;
public String getPassword() throws RemoteException;
}
远程Home接口UserRemoteHome:对应本地Home接口。之所以造成本地接口与远程接口的分离,是因为EJB原本是为分布式应用设计的,开始根本没有考虑本地访问的情况,因此在EJB1.1的规范中只有远程接口,后来因为性能和需求的关系,才在EJB2.0中增加了本地接口。具体的代码如下。
代码8.4 远程Home接口
package userejb;
import javax.ejb.EJBHome;
import javax.ejb.CreateException;
import java.rmi.RemoteException;
import javax.ejb.FinderException;
public interface UserRemoteHome
extends EJBHome {
public UserRemote create(Integer userId) throws CreateException,
RemoteException;
public UserRemote findByPrimaryKey(Integer userId) throws FinderException,
RemoteException;
public UserRemote findByUserName(String username) throws FinderException, RemoteException;
}
Bean类UserBean:注意是abstract class,容器是根据这个类去实现具体逻辑,代码如下。
代码8.5 Bean类
package userejb;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.CreateException;
import javax.ejb.RemoveException;
public abstract class UserBean
implements EntityBean {
EntityContext entityContext;
public Integer ejbCreate(Integer userId) throws CreateException {
setUserId(userId);
return null;
}
public void ejbPostCreate(Integer userId) throws CreateException {
}
public void ejbRemove() throws RemoveException {
}
public abstract void setUserId(Integer userId);
public abstract Integer getUserId();
public abstract void setUsername(String username);
public abstract String getUsername();
public abstract void setPassword(String password);
public abstract String getPassword();
public void ejbLoad() {
}
public void ejbStore() {
}
public void ejbActivate() {
}
public void ejbPassivate() {
}
public void unsetEntityContext() {
this.entityContext = null;
}
public void setEntityContext(EntityContext entityContext) {
this.entityContext = entityContext;
}
}
其中,各个方法的作用和操作方法可以参考专门的EJB著作。这里要说明的是,虽然可以直接编码访问这些接口,但是其实有更简单的方法,就是JBuider2005提供的配置生成的Facade模块,下面就来实现这个Facade模块。
8.4.4 实现Facade模块
Facade(门面模式)是J2EE实践中常用于封装EJB访问的设计模式,作用类似于代理。实际上,JBuilder2005实现的Facade模块综合运用了各种设计模式,来实现简化操作的目的。Facade模块的另外一个重要作用是:将对EJB的细粒度的get/set操作封装在本地实现,从而大大减少远程调用的次数和提升操作的粒度尺寸。实现这个Facade模块的具体操作如下。
(1)在实体Bean的名称“User”上右击,在弹出的菜单中选择【Launch DTO/Facade Wizard】命令,如图8-55所示。
(2)弹出Facade配置向导,如图8-56所示,虽然该向导一共有7步,但是一般的应用并不需要修改其中的任何内容,因此这里直接跳过,有兴趣的读者可以仔细查看其详细配置,前提是对各种常用的J2EE设计模式比较熟悉。

图8-55 Launch DTO/Facade Wizard 图8-56 Facade配置向导
(3)直接单击【Finish】按钮,在EJB模块白板中就出现了该实体Bean对应的会话Bean,即J2EE模式中常见的“会话门面(Session Facade)”模式的操作接口,如图8-57所示。同时工程中生成了一系列的新类,如图8-58所示。

图8-57 会话门面 图8-58 工程中生成了一系列的新类
(4)找到其中一个ServiceLocator类,修改其中的getInitialContext( )方法如下,修改的内容就是将user和password从null设置为服务器的访问账户,这样可以方便远程访问的实现(实际上本示例用户用不到远程访问)。
代码8.6 ServiceLocator类的getInitialContext( )方法
public Context getInitialContext() throws Exception {
String url = "t3://localhost:7001";
//注意修改该代码,其他不必关心
String user = "kingbill";
String password = "22222222";
Properties properties;
try {
properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
properties.put(Context.PROVIDER_URL, url);
if (user != null) {
properties.put(Context.SECURITY_PRINCIPAL, user);
properties.put(Context.SECURITY_CREDENTIALS,
password == null ? "" : password);
}
return new javax.naming.InitialContext(properties);
}
catch (Exception e) {
System.out.println("Unable to connect to WebLogic server at " + url);
System.out.println("Please make sure that the server is running.");
throw e;
}
}
前面说到,访问会话门面模式的操作接口是其中的会话Bean,即UserSessionFacade接口,但是实际上并不需要关心这个会话Bean,需要关心的是如下两个类:UserDto和UserDelegate。
1.UserDto类
UserDto类的代码如下。
代码8.7 UserDto.java
package userejb;
import java.io.Serializable;
//只需要看属性和setter/getter方法,其他不必关心
//传输对象必须实现Serializable接口
public class UserDto
implements Serializable {
private Integer userId;
private String username;
private String password;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (! (obj instanceof UserDto)) {
return false;
}
UserDto that = (UserDto) obj;
if (! (that.userId == null ? this.userId == null :
that.userId.equals(this.userId))) {
return false;
}
if (! (that.username == null ? this.username == null :
that.username.equals(this.username))) {
return false;
}
if (! (that.password == null ? this.password == null :
that.password.equals(this.password))) {
return false;
}
return true;
}
public int hashCode() {
int result = 17;
result = 37 * result + this.userId.hashCode();
result = 37 * result + this.username.hashCode();
result = 37 * result + this.password.hashCode();
return result;
}
public String toString() {
String returnString = "";
returnString += userId;
returnString += ", " + username;
returnString += ", " + password;
return returnString;
}
}
2.UserDelegate类
UserDelegate类的代码如下。
代码8.8 UserDelegate.java
package userejb;
public class UserDelegate {
private UserSessionFacadeHome userSessionFacadeHome;
private UserSessionFacade userSessionFacade;
public UserDelegate() throws Exception {
initializeUserSessionFacadeHome();
userSessionFacade = userSessionFacadeHome.create();
}
//需要关心的CRUD方法
public void createUser(UserDto userDto) throws Exception {
userSessionFacade.createUser(userDto);
}
public void removeUser(Integer userId) throws Exception {
userSessionFacade.removeUser(userId);
}
public void removeUser(UserDto userDto) throws Exception {
userSessionFacade.removeUser(userDto);
}
public void updateUser(UserDto userDto) throws Exception {
userSessionFacade.updateUser(userDto);
}
public void updateUsers(UserDto[] userDtos) throws Exception {
userSessionFacade.updateUsers(userDtos);
}
public UserDto userFindByPrimaryKey(Integer userId) throws Exception {
return userSessionFacade.userFindByPrimaryKey(userId);
}
public UserDto userFindByUserName(String _username) throws Exception {
return userSessionFacade.userFindByUserName(_username);
}
private void initializeUserSessionFacadeHome() throws Exception {
String FACADE_NAME = "UserSessionFacade";
Class FACADE_CLASS = userejb.UserSessionFacadeHome.class;
if (userSessionFacadeHome == null) {
try {
ServiceLocator locator = ServiceLocator.getInstance();
userSessionFacadeHome = (UserSessionFacadeHome) locator.getEjbHome(
FACADE_NAME, FACADE_CLASS);
if (userSessionFacadeHome == null) {
throw new Exception("Did not get home for " + FACADE_NAME);
}
}
catch (ServiceLocatorException e) {
throw new Exception(e.getMessage());
}
}
}
}
这两个类同前面章节中的UserDTO类和UserDAO类很相似,最后直接使用这两个类来“连接”到登录示例的代码中,从而实现DAO模式的各个接口。当然,这是为了本书内容的设置要求,实际上在实际的开发中可以直接使用这两个类来完成持久层的功能。
8.4.5 部署
接下来要做的是让这个EJB模块提供服务,具体来说,就是编译并且部署到服务器中。这样,只要服务器处于运行状态,各种应用就可以通过本地或者远程的访问,来使用其提供的持久层服务了。具体的步骤如下。
(1)编译。在工程图标上右击,选择【Make】菜单编译该工程,如图8-59所示。如果没有出错,会提示编译成功,如图8-60所示。同时出现一个user.jar文件,如图8-61所示。
(2)部署。在模块图标上右击,选择【Deploy Options for “user.jar”】|【Deploy】菜单

图8-60 编译成功 图8-61 user.jar文件

图8-62 部署 图8-63 部署成功
部署该模块,如图8-62所示,如果没有出错,会提示部署成功,如图8-63所示。同时在Weblogic控制台界面会出现该模块,如图8-64所示。

图8-64 控制台界面
8.4.6 测试
接下来简单地演示该模块的使用方法,步骤如下。
(1)创建一个测试工程“UserEJBTest”,如图8-65所示。

图8-65 创建测试工程UserEJBTest
(2)选择服务器为Weblogic8.1,如图8-66所示。
(3)将UserEJB工程生成的user.jar导入该工程,如图8-67、图8-68所示。
图8-66 选择服务器

图8-67 将user.jar导入该工程

图8-68 将user.jar导入该工程
(4)创建一个测试类UserTest,代码如下。
代码8.9 测试类UserTest.java
import userejb.UserDto;
public class UserTest {
public static void main(String[] args) {
try{
//创建UserDelegate实例
UserDelegate ud = new UserDelegate();
//调用CRUD方法
UserDto u=ud.userFindByUserName("kingbill");
//输出内容
System.out.println(u.toString());
}catch(Exception e){}
}
}
(5)这时候会提示UserDelegate类不存在,同时检查user.jar中也不存在该类。JBuilder编译的时候并不将该类同其他类一起打包,因此将UserEJB工程中的UserDelegate类复制到本工程下。这里需要删除package声明,同时导入所有user.jar中的类和接口,代码如下。最后的工程结构如图8-69所示。
代码8.10 复制的UserDelegate.java
//package userejb;
import userejb.*;
public class UserDelegate {
……
}
(6)运行测试类,结果如图8-70所示。

图8-69 工程结构 图8-70 运行测试类







