欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

J2EE EJB3.0 Specification 开发

程序员文章站 2022-07-15 14:23:51
...
简介

J2EE架构中的EJB框架是多层架构(multi-tier architecture) 所提供的服务机制(简称后台),而EJB也是J2EE讨论中离不开的题目。在J2EE的整个发展过程当中,EJB可以大概分为两个主要的版本,分别是2.1与3.0. 简单的说,EJB2.1对开发者的规范要求是非常多的,相反,在EJB 3.1的环境中开发就变得简单得多。WEBSTONE的DRP第二个版本开发将会采用EJB3为后台的开发基础。本文是针对EJB3.0而写的,所以不会包含EJB2.1的讨论。如果有兴趣可以在网上找到很多相关的资料。

EJB

EJB也是Enterprise Java Bean 的简称。EJB分为以下三种: 对话(Session) 、实体(Entity) 和消息(Message Driven) Bean。Session bean 实现企业的业务逻辑(business logic)。Entity bean 代表业务对象(business object)。MDB 是用来处理和消化ASYNC MESSAGE。

EJB3的特点

 基于Java POJO
 简化了对HIBERNATE的操作,更不需要DAO设计
 简化了MODEL与ENTITY之间的转换


例子一、  尺码管理

尺码组(Size Group)与尺码(Size) 是服装的基础信息之一。它们之间的关系是一对多。尺码组的名称和序号必须是唯一;每个尺码组中的尺码名称和序号也必须是唯一的(见下图)

(一般而言服装公司的尺码是不容易变动的。把尺码信息存放在数据库中的目的是方便维护。系统起动后尺码信息该从Enumeration 来读取)

A S M L XL
B 44 46 48 50 52
C 70 72 74
图一、服装尺码


图二、尺码对象分析




(Class) 属性
(Attributes) 数据类型
(Data Type) 说明 规则
Size
id Long 主键 系统自动生成
name String 尺码名称 不能为空
order int 尺码的次序 >0 && <尺码总数
SizeGroup id Long 主键 系统自动生成
name String 尺码组名称 不能为空
startPos int 尺码的开始位置 >=0 && <尺码总数 – 1
order int 尺码组次序 >0 && <尺码组总数
表一、尺码/组数据字典

尺码组与尺码用Entity Bean来实现

尺码管理有以下的用例:

用例用session bean来实现

构建Entity bean

在定义了类之间的关系后,我们便可以开始构建Entity bean.



Size.java (POJO with annotation):

@Entity
@Table(
name = "MASTER_SIZE",
uniqueConstraints={@UniqueConstraint(columnNames={"seq", "name", "fk_sg_id"})}
)
public class Size implements Serializable{

private static final long serialVersionUID = 1L;

private Long id;
private int seq;
private String name;
private SizeGroup sizeGroup;

public Size () {

}

public Size (String name, int seq, SizeGroup sg){
this.setSeq(seq);
this.setName(name);
this.setSizeGroup(sg);
}

public int getSeq() {
return seq;
}

public void setSeq(int seq) {
this.seq = seq;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the id.
*/
@Id
@GeneratedValue
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
@ManyToOne
@JoinColumn (name="fk_sg_id")
public SizeGroup getSizeGroup() {
return sizeGroup;
}
public void setSizeGroup(SizeGroup sizeGroup) {
this.sizeGroup = sizeGroup;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Size order:")
.append(seq)
.append(" name:")
.append(name)
.append(" group:")
.append(sizeGroup.getName());
return sb.toString();
}
}

程序员对Java POJO不会感到迫生,可是对于以“@”符号为开头的语句可能感到不太习惯。这些符号代表的是注解(ANNOTATION)。注解是在JDK1.5开始所加入的一个新特性,在这里起到了简化XML的作用。



在Size这个类有几点我们要注意的。
1. @Entity  标示这个POJO是一个实体bean
2. @Table  标示这个实体所对应的表名和属性 (MASTER_SIZE)
3. @UniqueConstraint  标示这个实体必须是唯一的属性
4. @Id  与数据库设计一样,每个实体都必须有一个主键。而SizeBean的主键是id。
5. @ManyToOne  Size与SizeGroup之间的关系是多对一,用栏位fk_sg_id来关连。

就这样便完成了Size这个实体bean的构建了。SizeGroup与Size大同小异。

SizeGroup.java :

@Entity
@Table(
name="MASTER_SIZE_GROUP",
uniqueConstraints={@UniqueConstraint(columnNames={"namexxx"})}
)
public class SizeGroup implements Serializable{

private static final long serialVersionUID = 1L;

private Long id;
private int seq;
private String name;
private int startPos;
private Collection<Size> sizes;
…..
@OneToMany (mappedBy="sizeGroup", fetch=FetchType.LAZY)
public Collection<Size> getSizes() {
return sizes;
}
}

一个SizeGroup 可以有多个Size。所以Size是一个集成 Collection. 而且在getSizes()要用@OneToMany来说明这是个一对多的关系。

以下是在EJB3常用到的ANNOTATION:

@Entity  (表示一个实体BEAN)
@Stateless  (表示一个无状态的对话BEAN)
@Stateful  (表示一个有状态的对话BEAN)
@Table  (表示实体BEAN对应在DB中的表名和其他属性)
@Id (表示实体BEAN的主键 primary key)
@GeneratedValue  (主键产生方法)
@ManyToOne (实体之间多对一的关系描述)
@OneToMany (实体之间一对多的关系描述)
@OneToOne (实体之间一对一的关系描述)
@ManyToMany (实体之间一对一的关系描述)
@Remote (表示remote接口)
@Local  (表示本地接口)



构建Session bean

实体建立后,我们便可以开始构建业务逻辑。





Session bean由接口和bean所组成。

接口(API)定义了业务逻辑的输入和输出。 接口是采用Java interface来定义的.

远程(Remote) &本地(Local) 接口
J2EE有两种调用EJB的方法,分别是本地(Local) 和远程(Remote) 调用。它们的分别是远程调用要通过RMI的转换,而本地调用就不需要。为了提高性能,一般的WEB应用都会建议使用本地调用。可是为了方便未来的需要,SizeBean同时实现了两种接口。

跟据上文的用例分析,我们得出以下的API:

package com.wst.drp.fashion.svc;
import java.util.Collection;
import com.wst.drp.fashion.entity.Size;
import com.wst.drp.fashion.entity.SizeGroup;

public interface SizeService {

/**
* Add a new size group.
* @param name Size group name must be unique
* @param order Size group order must be unique
* @throws  SizeServiceException if size group name already exist
*/
public void addSizeGroup (String name, int order)
throws SizeServiceException ;

/**
* Create a new size group & set the order to maximum order + 1
*
* @param name Size group name must be unique
* @throws SizeServiceException if size group name already exist
*/
public void addSizeGroup (String name)
throws SizeServiceException;

/**
* Returns a size group
*
* @param groupId
* @return null if not found
*/
public SizeGroup getSizeGroup(Long groupId);

/**
* Create a new size
*
* @param name Size name must be unique
* @param order Size order
* @param groupId
* @throws SizeServiceException if size name already exist
*/
public void addSize(String name, int order, Long groupId)
throws SizeServiceException ;

/**
* Create a new size & set the order to maximum order + 1
* @param name
* @param groupId
* @throws SizeServiceException if size name already exist
*/
public void addSize (String name, Long groupId) throws SizeServiceException ;

/**
* Return a size
* @param sizeId
* @return
*/
public Size getSize (Long sizeId);

/**
*
* @return
*/
public Collection<SizeGroup> getAllSizeGroups();

/**
*
* @param groupId
* @return
*/
public Collection<Size> getSizesOfGroup(Long groupId);

/**
* Returns max group size
* @return
*/
public int getMaxGroupSize();

/**
* Returns min group size
* @return
*/
public int getMinGroupSize();

/**
* Delete a size group
* @param groupId
* @throws SizeServiceException if 'groupId' does not exist
*/
public void deleteSizeGroup(Long groupId) throws SizeServiceException ;

/**
* Delete a size
* @param sizeId
* @throws SizeServiceException if 'sizeId' does not exist
*/
public void deleteSize(Long sizeId) throws SizeServiceException;

}

Session API的设计

业务系统API主要是实现业务逻辑,而API设计的种点是简单化、清晰和独立操作。另外,API设计的好与坏也决定于需要处理的数据量,如果要返回过多数据或者中间需要经过多次转换,对维护和性能会带来重大影响。

package com.wst.drp.fashion.ejb;
// ignored import statements

@Stateless
public class SizeBean implements SizeServiceLocal, SizeServiceRemote {
   @Persistence Context (unitName = "abcdb")
   private EntityManager em;

public void addSizeGroup(String name, int order, int startPos)
throws SizeServiceException {

if (name == null || name.equals("")) {
throw new SizeServiceException ("尺码组名称不能为空");
}

if (this.containsSizeGroupWithName(name)) {
throw new SizeServiceException ("尺码组名 '" + name + "' 已存在");
}

// assert order < 0
// assert startPos < 0

SizeGroup newSG = new SizeGroup (name, order, startPos);
em.persist(newSG);
}

// 判断组名name是否已经被使用
private boolean containsSizeGroupWithName (String name) {
List list = em.createQuery(
        "from SizeGroup sg where sg.name = :name")
         .setParameter ("name", name)
         .getResultList();
if (!list.isEmpty()) {
return true;
}
return false;
}
…..
}

@Stateless 说明了这是一个无状态的session bean
@Persistence 指向持久配置设定

从需求中我们知道尺码组的名称不可以相同,所有在进行addSizeGroup()方法前我们要判断name是否已经被使用。如果已被使用,程序就要抛出异常通知用户端并说明原因。

containsSizeGroupWithName()方法是用来判断组名name是否已经被使用。这个方法采用了EJBQL语法来查询数据库。EJBQL的语法与Hibernate 的查询语法很相似。这种查询方法在开发应用时常常用到。

如果组名没有被使用,便可以增加组。操作和Hibernate 的操作是一样的。创建一个新的对象,然后再通过EntityManager来持久到数据库中。EntityManager 就同等于Hibernate里的HibernateSession。

你也许发现EntityManager 是不用创建或者通过某些方法来获取的。其实是因为使用了@PersistenceContext的注解声明:

   @PersistenceContext(unitName = "abcdb")
   private EntityManager em;

在JBOSS中EntityManager是由EJB容器根据用户配置的data source为entity beans提供entityManager实例,所以,用注解声明persistenceContext(unitName=”datasource name”) 详情可以参考 “Webstone JBOSS EJB3 Development Guide”


客户端(Client)
Work in progress

小结:

你可能会发现采用EJB架构的优点是简单化很多设计的工作,包括对DAO层和ENTITY-TO-MODEL的设计要求。以下让我们再来看看怎样用EJB架构来设计业务系统。

例字二  -  款式管理

款、色、码是服装的三个基本原素。款和色、码之间的关系是多对多。换句话说,一个款可以有多个颜色,同一个颜色可以重复在不同的款中。虽然一个款可以有多个尺码,可是也局限於一个尺码组中的尺码。下图说明了款、色、码、尺码组之间的关系和属性的规则。


图三、款式定义


(Class) 属性
(Attributes) 数据类型
(Data Type) 说明 规则
Style
id Long 主键 系统自动生成
no String 款号 必须唯一
name String 款名 不能为空
Color id Long 主键 系统自动生成
no String 色号 必须唯一
name String 色名 不能为空