指引网

当前位置: 主页 > 编程开发 > Java >

EJB 2.x 过时了吗?

来源:网络 作者:佚名 点击: 时间:2017-11-14 06:39
[摘要] 理论:除旧迎新 嗨!EJB 倡导者, 该醒醒,闻闻咖啡的香味了!EJB 2.x 已经过时!EJB 3 的时代到来了!来看一下,当创建一个持久性对象(例如您前面的一篇文章中的 customer 对象)时,事情变得
EJB

  理论:除旧迎新

  嗨!EJB 倡导者,

  该醒醒,闻闻咖啡的香味了!EJB 2.x 已经过时!EJB 3 的时代到来了!来看一下,当创建一个持久性对象(例如您前面的一篇文章中的 customer 对象)时,事情变得简单多了:


@Entity
@Table(name="CUSTOMER")
public class Customer implements Serializable {
	
	private int id;
	private String name;
	private Order openOrder;
	
public Customer () {
super();
}
	
	@Id
	@Column(name="CUST_ID")
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
	@Column(name="NAME")
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@OneToOne(cascade=CascadeType.ALL , fetch=FetchType.EAGER )
	@JoinColumn(name="OPEN_ORDER_ID",referencedColumnName="ORDER_ID") 
	public Order getOpenOrder() {
		return openOrder;
	}
	public void setOpenOrder(Order openOrder) {
		this.openOrder = openOrder;
	}

}

  我只需要创建一个简单的 POJO 并对它进行注释。而不必继承任何东西。不需要任何独立的 Home、接口或部署描述符。并且不必执行特定于供应商的映射步骤!

  使用 EJB 3 的客户端程序中的代码甚至更简单。假定我想要获得与客户(其密钥是一个整数,称为 customerId)相关联的未完成订单 (Open Order):


@PersistenceContext (unitName="db2")
EntityManager em;
try {
	Customer c = (Customer)em.find(
		Customer.class, new Integer(customerId)
	);
	Order o = c.getOpenOrder();
	return o;
}
catch (EntityNotFoundException e) {
	throw new CustomerDoesNotExist(customerId);
}

  因为有一个 EntityManager 实例作为“Home”提供给所有实体,所以不需要进行 JNDI 查找。这再简单不过了。

  所以请告诉我,为什么有烤肉吃了还要回头啃汉堡呢?

  署名:
  Never Going Back

 

  实践:没有那么快,还要考虑折衷

  亲爱的 Never Going Back:

  您的见解有些过头,因此我想借此机会(不客气地)指出,您所提到的实体 EJB 组件实际上是 Java Persistence API (JPA) 的一部分,它是独立的,但是相关的规范是在 JSR 220 的基础上创造的。所以,更准确的说法是“使用 JPA 更加简单”。

  不过说实在的,您能发现可靠的持久性标准方法我很高兴。我接触过的许多人都没有创造自己的持久性方法。我认为您署名“Never Going Back”中的“Back”表明您不在这些人之列。

  也就是说(又有可能要冒犯您了),EJB Advocate 将会细心地分析您指出的优点,所以您可以实际对比一下 JPA 与 CMP 实体组件的好处:

  1. JPA 实体组件不是 POJO。

    您最初提到您只是创建一个 POJO 并对它进行注释(在其他内容之间加注 @entity),以此创建 JPA 样式实体。这是正确的。但是严格地说,大多数时候不要考虑让持久性机制基于 POJO,除非可以在现有的(未修改的)简单 Java 类中使用它。

    您可能也回想过 EJB 1.x 时期,当时实体组件更像是 POJO。它将实例变量声明为 Bean 实现(一个实际的类)的一部分。您可以通过将其声明为实体 EJB 来“注释”该类,并实现生命周期方法,如 ejbLoad() 和 ejbStore()(或者将它们交由部署工具实现)。此方法被废弃的一个原因是它使 CMP 字段要么全部被加载,要么全部不被加载。EJB 2.x 方法允许灵活地进行性能增强,例如将 get<Property>() 方法映射到数据库指针或流实现上,以降低转换次数。JPA 允许通过注释来“懒散地”加载属性,但正如我们在后面看到的,当考虑到实体是一个 POJO 时,此方法自身也有问题。

  2. JPA 和 EJB 2.x 都不要求您的子类扩展任何特殊类。

    然后您提到当使用 JPA 时不需要“继承”任何东西,这意味着 EJB 2.x 实体需要继承。这种推断是不正确的。我们考虑一个典型的 EJB 2.x 实体组件类声明:

    
    public abstract class CustomerBean implements EntityBean ...

    一个类(不管是抽象的还是具体的)实现一个接口并不真正从它实现的接口继承任何东西。实现一个接口只是简单地标记该类可以在应用程序中扮演指定的角色——就像 @entity 注释所要实现的目的。

    这种差别十分重要,因为对于实现,Java 只支持单一继承。您可能只扩展一个类并继承其方法实现。您可以实现任意多的接口。

    因为 EJB 2.x 实体是一个抽象类,所以与 EntityBean 接口相关联的各种 EJB 生命周期方法(如 ejbLoad() 和 ejbStore())都不需要实现。这些实现可以交给供应商提供的部署工具来完成。

  3. JPA 实体组件不支持多视图。

    接下来,您提到您不需要对 JPA 实体创建接口或 Home。这是对的。但这种简单性是有代价的:您需要直接访问实现类。其局限性是您只能获得组件的单一视图。Java 接口的强大之处在于您可以提供按客户端需要定制的实现组件的视图。EJB 2.x 实体组件延续实现独立于其支持的接口这种概念。例如,您可以同时提供一个实体的远程视图和本地视图。

    当已知的最佳实践是使用 Facade 时,为什么您还要提供实体 EJB 组件的远程接口呢?在前面的 EJB 倡导者文章中,我们讨论了如何利用实体组件上的自定义 Home 方法来作为 Facade 上的构建。由于 Facade 通常是远程使用的,所以可以提供一个远程的 Home 接口,它包含如下所示的一个或多个自定义方法:

    
    public interface CustomerHome implements EJBHome {
    	CustomerData ejbHomeGetOpenOrderForCustomer(int customerId) ...
    }

    请注意,此远程接口并没有任何 create() 或 find() 方法。这种有意遗漏可以确保 Customer 实体的实例永远不会被远程访问。相同的 Bean 实现的本地接口和 Home 将公开 create 和 find 方法以及适当的 get 和 set 方法(它们可能代表 CMR,如另一篇 EJB 倡导者文章中所描述的)。

    也就是说,EJB 相关工具(如 IBM Rational Application Developer for WebSphere 软件)的大多数供应商都通过向导工具一次性生成 Home 和接口。一些供应商甚至能够从 Bean 实现开始(例如 POJO),根据所期望的将方法升级到接口和 Home。

  4. JPA 实体组件将实现细节加入您的 POJO 中。

    您宣称的 JPA 的另一个“优点”是您可以不用创建独立的部署描述符,而且可以跳过特定于供应商的映射步骤。这固然很好(特别是映射到关系数据存储的标准映射部分),但也有折衷之处。您将实现细节嵌入到代码中,这使得对象更不像您所渴望的简单 POJO。

    EJB 2.x 实体组件的最佳特性之一是部署器可以修改使给定的业务对象的持久性完全独立于 Bean 提供者的细节的方式。供应商无理由不能采用类似的映射注释标准集,将其作为 EJB 2.x 样式部署描述符的一部分,以简化映射步骤。

  5. JPA 实体组件仍然在 Facade 之后用得最好。

    最后,您正确地指出 EntityManager 实现只有一个,这就不需要进行 JNDI 查找,并使得使用 JPA 实体组件变得异常简单。然而,您没有注意到,如果没有在事务上下文中使用 JPA 实体,则需要显式保存到 EntityManager 以保存任何更改。您没有提到的JPA 的另一个方面是,如果您在注释中将实体声明为“懒散”加载,随后又将其与上下文分离(也就是该事务结束),则未改变的字段就是未定义的。

    必须显式管理事务上下文将增加 JPA 编程模型的复杂性。所以使用会话 Facade(不管是新的 EJB 3 样式还是旧的 EJB 2.x 样式)仍然是最佳实践。如果您选择从 Facade 返回 POJO,则需要将加载设置为“eager”或者改动 JPA 实体中的所有字段。对于 EJB 2.x 自定义 Home 方法,会话不是绝对必需的。

  正如您可以看到的,您提到的 EJB 3 和 JPA 的大多数好处都包括简化假设,这可能无法适用于所有情况。您必须自己决定是否可以接受这些假设。在您可以接受的情况下,我希望您看到通过部署工具,几乎所有好处都可以应用到 EJB 2.x 规范。最后,甚至连 JPA 规范都提到在 EJB 3 和 JPA 定稿后仍然期望使用 EJB 2.x。

  因此(向 Mark Twain 致以歉意),EJB 倡导者相信 EJB 2.x 会过时的谣传有些言过其实。

  好的,就先到此为止,
  您的 EJB 倡导者

 

  结束语

  这篇采用对话形式的文章讨论了关于简单性的一些基本问题。例如,是拥有一个能带给您所有需要的灵活性的复杂系统好呢?还是拥有一个能轻松应付一般情况的简单系统好呢?在前面的思考中,EJB 倡导者琢磨着:为什么不能够二者兼顾:既有灵活性又有一种缺省情况来使事情简单化呢?

  对于那些喜欢用面向对象的方法来进行应用程序开发的人而言,EJB 2.x 也许仍是更好的方法,因为它利用了 Java 的所有特性(这些特性铸就了 Java 语言的强大),特别是将接口与实现分离的特性。

------分隔线----------------------------