EJB组件的一个比较常见的用途是在关系型数据管理领域。与RMI结合起来,EJB组件让您不必钻研JDBC就可以从关系数据库访问数据。但这种抽象是要付出代价的:RMI很慢,通常是极慢。那么,窍门就是找到一种方法来保持EJB技术的所有优点而又没有使用RMI的巨大开销。在这篇技巧文章中,您将看到值对象(也称为对象映射)是如何帮助您绕开最严重的RMI速度障碍的。您将首先从一个工作示例开始,然后了解代码是怎样工作的。
简单实体bean
考虑一个名为DVDs的简单数据库表。这个表有几列:id、title、releaseDate、producer(通过外键)和director(也是通过外键)。因为我们正在使用EJB组件,所以表由实体bean表示,并且每个列都有其自己的取值(accessor)方法和赋值(mutator)方法。清单 1 显示了我们的DVD表的远程接口:
清单 1. DVD远程接口
以下是引用片段: 1 import com.ibm.ejb; 2 import java.rmi.RemoteException; 3 import java.util.Date; 4 import javax.ejb.EJBObject; 5 public interface DVD extends EJBObject { 6 public int getId() throws RemoteException; 7 public String getTitle() throws RemoteException; 8 public void setTitle(String title) throws RemoteException; 9 public Date getReleaseDate() throws RemoteException; 10 public void setReleaseDate(Date releaseDate) throws RemoteException; 11 public Person getProducer() throws RemoteException; 12 public void setProducer(Person producer) throws RemoteException; 13 public Person getDirector() throws RemoteException; 14 public void setDirector(Person director) throws RemoteException; 15 } |
这里的问题是如何访问表数据,可能一次访问所有数据。为进行一次DVD销售或搜索,在线商店或清单应用程序很可能要求获取上述列中包含的大多数或所有信息。为了访问所有信息,应用程序为每列调用一个取值方法 ― 共有五次方法调用,每次都会多占用一点RMI通信时间。这种情况再加上其它复杂性,如可能的错误情况、网络流量和相关问题,以及数据的指数级数量(大多数此类表有15行或更多),我们的应用程序随时都可能崩溃。
这时就需要值对象。值对象是简单的Java类,可以用它来表示多种对象,包括关系数据库行中的数据。通过直接使用值对象,而不是反复使用bean的远程接口,我们可以将RMI通信减少到一次方法调用。
创建值对象
清单 2 中的值对象看起来和我们的远程接口几乎相同,但它实际上是具体类。注:通常用bean的名称加上Info来表示值对象。
清单 2. DVD值对象
以下是引用片段: 1 package com.ibm.ejb; 2 import java.io.Serializable; 3 import java.util.Date; 4 public class DVDInfo implements Serializable { 5 private int id; 6 private String title 7 private Date releaseDate; 8 private Producer producer; 9 private Director director; 10 public int getId() { 11 return id; 12 } 13 void setId(int id) { 14 this.id = id; 15 } 16 public String getTitle() { 17 return title; 18 } 19 public void setTitle(String title) { 20 this.title = title; 21 } 22 public Date getReleaseDate() { 23 return releaseDate; 24 } 25 public void setReleaseDate(Date releaseDate) { 26 this.releaseDate = releaseDate; 27 } 28 public Person getProducer() { 29 return producer; 30 } 31 public void setProducer(Person producer) { 32 this.producer = producer; 33 } 34 public Person getDirector() { 35 return director; 36 } 37 public void setDirector(Person director) { 38 this.director = director; 39 } 40 } |
您应该看出这个类的两个特点。首先,它实现了java.io.Serializable。任何可以被实体bean(或任何其它EJB组件)返回的对象都必须满足这个要求。其次,该类中没有方法能够抛出RMI RemoteException 。这个对象不需要RMI通信(这是本练习的全部要点!),因此不会发生RemoteException。否则,值对象就成为bean的远程接口的翻版了。
添加两个新方法
创建值对象类是我们的RMI解决方案的第一个部分。第二个部分是将两个有价值的方法添加到我们的远程接口,如清单 3 所示:
清单 3. 已修改的DVD远程接口
以下是引用片段: 1 import com.ibm.ejb; 2 import java.rmi.RemoteException; 3 import java.util.Date; 4 import javax.ejb.EJBObject; 5 public interface DVD extends EJBObject { 6 public DVDInfo getInfo() throws RemoteException; 7 public void setInfo(DVDInfo info) throws RemoteException; 8 public int getId() throws RemoteException; 9 public String getTitle() throws RemoteException; 10 public void setTitle(String title) throws RemoteException; 11 public Date getReleaseDate() throws RemoteException; 12 public void setReleaseDate(Date releaseDate) throws RemoteException; 13 public Person getProducer() throws RemoteException; 14 public void setProducer(Person producer) throws RemoteException; 15 public Person getDirector() throws RemoteException; 16 public void setDirector(Person director) throws RemoteException; 17 } |
接下来,也是最后一步,在我们的bean的实现类中实现getInfo()和setInfo()这两个新方法,如清单 4 所示:
清单 4. 已修改的DVD远程接口
以下是引用片段: 1 // Rest of class excluded for brevity 2 public DVDInfo getInfo() throws RemoteException { 3 DVDInfo info = new DVDInfo(); 4 // Load value object with current variable values 5 info.setId(this.id); 6 info.setTitle(this.title); 7 info.setReleaseDate(this.releaseDate); 8 info.setProducer(getProducer()); 9 info.setDirector(getDirector()); 10 return info; 11 } 12 public void setInfo(DVDInfo info) throws RemoteException { 13 setTitle(info.getTitle()); 14 setReleaseDate(info.getReleaseDate()); 15 setProducer(info.getProducer()); 16 setDirector(info.getDirector()); 17 } |
“魔术”是如何实现的
我们的应用程序需要能够访问来自DVDs表的DVD bean中的所有数据。但是,我们并不调用所有的五个取值方法,而是设置应用程序,只调用一个方法:getInfo()。这大大减少了我们的RMI通信。
bean的实现类在“幕后”调用所有相同的取值方法。但是,因为它们在EJB容器中发生,所以它们是本地调用。但是,所有数据仍将传到 bean客户机,因此仍可以使用这些数据。如果需要对数据进行任何修改,我们可以仅用setInfo()方法将它们传回bean,而不是使用四个或五个开销很大的RMI调用。
这种方法唯一的缺点是:有获得旧数据的轻微的风险。如果在内存中将值对象保持一段时间,您就会冒这种风险。虽然值对象包含数据库中数据的快照,但它不能动态地反映数据的更改。避免旧数据的最佳方法是立即使用值对象。如果稍后您需要再次使用它,则应该花一次 RMI 开销来再次调用getInfo() ,以确保可以使用最新数据。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
分析Java语言的复杂数据类型
除了简单数据类型之外,Java语言中还定义了三种索引数据类型,你知道它们是什么吗?如何初始化过程、默认初值和针对复杂数据类型操作的呢?
-
基于Java开发:关于类设计技巧的四点建议
在Java面向对象编程的过程中,类的设计是一个很有挑战性的工作,不同的人可能对于类的理解不一样。但是还是有一些可以借鉴的东西。
-
如何在Spring Web MVC环境下使用DWR?
目前Ajax的开发框架有很多,使用这些框架可以简化Ajax的开发。DWR (Direct Web Remoting)是一个用于改善Web页面与Java类交互的远程服务器端Ajax开源框架。
-
如何在java实施中使用SCA注释?
你想知道Java实施内服务、参照和属性是如何定义的吗?让我们使用支付的例子看看Java注释为Payment组件,如何被添加到Java类中来定义SCA服务……