服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

使用cmp2和xml处理动态数值对象

  在ejb 2.0局部引用和容器管理关系出现之前,通常把实体ejb用于模型粗粒度域对象。这主要是由于与远程通信有关系统开销并阻止了细粒度访问企业层的客户层对象。粗粒度设计的性能通过实现数值对象而促进改善,压缩所有的数据也就是说在客户层和企业层之间传递数据。使用有大量域对象的复杂系统,即使这么样能提供一个第一流的和高度执行设计,这个设计导致在系统内出现过多的数值对象。这同样也创建在企业层和客户层之间的紧密连接。同样,在ejb出现之间,bean提供者不得不明确地提供用于维护域对象之间联接的程序。在一个有在域对象之间的复杂关系的情况中,数值对象的设计变得很复杂。在使用ejb开发企业应用程序的过程中,容器管理关系和局部引用的出现开启了令人兴奋的新途径。在本文中,我将带你使用ejb 2.0的强大方法连同bean组件和jaxp创建动态的基于数据结构的xml,可以在你的企业层和表现层之间传递信息。

  从企业层到客户层传递数据的过程中使用xml,可以帮助你实现在的应用程序中的多种宽松连接;然而,当你把新的域对象添加到实体型中时,你可能需要添加用于创建新dom构架的类来添加该实体。在文中,我们将开发一个框架来动态地遍历容器管理和给定的局部ejb有关的域,并且创建一个可以在应用程序的多个层之间传递的xml件。这个方法将有以下优点∶

  在企业层和客户层之间促进宽松连接。

  便于管理域对象之间关系。

  从系统中除去复杂数值对象因为xml是由动态地遍历cmp和cmr域产生的,当它们添加新对象到域模型中时,bean提供者不必创建新的用来创建新的dom构架的对象类。

  ejb 2.0局部引用促进访问bean组件;bean组件与局部引用结合可以含于与其他的被容器管理的bean的关系。举例来说,在一个帮助系统中,userejb可以有与servicerequestejb的一到多双向关系和与productejb的一到一的单向联系以及与servicerequesthistoryejb的一到的双向关系。userejb同时可以有与phoneejb一到多的双向关系。这样,使用ejb 2.0局部引用和容器管理关系,你可以设计一套复杂的有关体。容器管理持久性和关系域是在bean类中使用抽象存取程序方法定的。用于关系域的存取程序方法要么返回一个集合要么返回定义这个系的bean的本机接口,这还取决于这个关系集的容量。这些存取程序法可以通过bean组件的本机接口展示。ejb 2.0的深入研究超出了本文范围,请参阅ejb 2.0的规范。

  一个效率高的设计模式应该通过外观组件展示你的应用程序的使用案例并且不准从客户层中直接访问实体组件。回到我们的帮助系统的例子中来,其中的一个使用案例是取得给定用户的详细资料。外部组件可以查找需要的用户实体组件,并且通过容器管理持久性与关系域取得所需数据并把它返回表现层。

  用于数据传送对象的一个显而易见的选择就是简单的java bean。userbean可能有表示持久性与关系域的属性。这种关系域要么是java.util.collection要么是其他的取决于这种关系的bean组件。userbean可能有servicerequestbean和phonebean集。servicerequestbean可能有一个productbean和许多servicerequesthistory bean组件。此外,这些bean组件还可能还有简单的string或者表现容器管理持久性域的基本属性。这个选择的主要的不利之处就是使你的实体模型更加复杂,使你的数据传送对象bean层次更加复杂,还将在你的服务(企业)层和消费(表现)层之间创建紧密连接。研究一下这个关系的复杂的层次,一个更好的选择是使用xml dom对象作为数据传送对象。你的组件将产生oeg.w3c.dom.document对象类型并且你的表现组件使用xml的jsp自定义标记和xslt文件来“消费”它们。


  现在,下一个问题是∶怎么从cmp局部bean引用中创建xml文件?我们可以使用一个基于用于创建不同的类型的dom组件的方法的工厂;然而,这将创建过多的“工场”组件,并且你可能需要添加新的"工场"组件作为你的实体模型扩展。我们需要的是一个可以使用实体局部对象,引导容器管理关系和创建动态dom构架的公用程序。这个公用程序将负责在双向关系中循环引用,以避免无限的循环和深入导航关系元素。

  接下来我们将演示一个可以提供这个功能的公用程序类。

package ws.business.service.util;
导入jaxp类。
import javax.xml.parsers.documentbuilderfactory;
import javax.xml.parsers.documentbuilder;
import javax.xml.parsers.parserconfigurationexception;
导入dom类
import org.w3c.dom.document;
import org.w3c.dom.element;
导入ejb类
import javax.ejb.ejblocalobject;
导入collection类。
import java.util.collection;
import java.util.arraylist;
import java.util.iterator;
导入bean内省类。<0}
import java.beans.introspector;
import java.beans.beaninfo;
import java.beans.propertydescriptor;
import java.beans.introspectionexception;
导入reflection类
import java.lang.reflect.method;
import java.lang.reflect.invocationtargetexception;
public class domgenerator {
生成的文件
private document doc;
用户定义的深度∶
private int drilldowndepth;
存储循环引用的私有实例变量∶
private arraylist _circularref = new arraylist();
存储当前深度的私有实例变量∶
private int _currentdepth;
方法getejblocalhome,getlocalhandle,getclass和getprimarykey不必
被处理。
private static string reserved =
"ejblocalhome^localhandle^class^primarykey";

public domgenerator(string docelementname, int drilldowndepth) {

try {
使用标准jaxp调用来创建一个dom并把它存储为一个实例变量。
documentbuilderfactory fact =
documentbuilderfactory.newinstance();
documentbuilder builder = fact.newdocumentbuilder();
doc = builder.newdocument();
}catch(parserconfigurationexception ex) {
throw new runtimechainedexception(ex);
}
存储drilldown深度。
this.drilldowndepth = drilldowndepth;
以用户定义的名称创建文件元素。
doc.appendchild(doc.createelement(docelementname));

}
创建传递dom的局部引用的方法。

public document getdom(ejblocalobject local) {

try {
调用把容器管理持久性与关系域填充到元素中以便传递局部引用的方
法。
populateelement(doc.getdocumentelement(), local);
返回dom。
return doc;
}catch(introspectionexception ex) {
throw new runtimeexception(ex.getmessage());

}catch(illegalaccessexception ex) {
throw new runtimeexception(ex.getmessage());
}catch(invocationtargetexception ex) {
throw new runtimeexception(ex.getmessage());
}

}

private void populateelement(element parent, ejblocalobject local)
throws
introspectionexception, illegalaccessexception,
invocationtargetexception
{

把当前的局部引用添加到连接引用列表中。
_circularref.add(local);
增加当前的drilldown深度。
_currentdepth++;
取得当前的局部引用的bean组件信息。
beaninfo info = introspector.getbeaninfo(local.getclass());
取得属性描述信息列表并重复数组。
propertydescriptor properties[] = info.getpropertydescriptors();

for(int i = 0;i < properties.length;i++) {
取得属性读取方法。
method propreadmethod = properties[i].getreadmethod();
取得属性名称。
string propname = properties[i].getname();
使用映射取得属性值。
object prop = propreadmethod.invoke(local, null);
跳过保留属性。
if(reserved.indexof(propname) >= 0)
continue;

try {
尝试把属性强制转化为ejb局部引用。请注意你不能使用instanceof,因
为从容器到容器使用instanceof,实现可能有差异。
ejblocalobject locprop = (ejblocalobject)prop;
如果局部引用已经在连接引用的列表中可用或者当前的drilldown深度
已经大于设置的drilldown深度那么就跳出处理过程。
if(iscircularref(locprop) || _currentdepth >=
drilldowndepth)
continue;
由属性创建一个元素。
element child = doc.createelement(propname);
populateelement(child, locprop);
把新创建的子元素添加到父元素中。
parent.appendchild(child);

}catch(classcastexception ex1) {

如果抛出一个classcastexception,就要试着把属性强制转换成collection
局部引用类型。请注意你不能使用instanceof,因为从容器到容器使用
instanceof,实现可能有差异。
try {

collection colprop = (collection)prop;

如果当前的drilldown深度大于设置的drilldown深度,跳出处理。
if(_currentdepth >= drilldowndepth) continue;
创建一个压缩collection的子元素。
element child = doc.createelement(propname);
iterator it = colprop.iterator();

while(it.hasnext()) {

得到collection中的每个局部引用。
ejblocalobject locprop =
(ejblocalobject)it.next();
如果局部引用已经在连接引用的列表中可用或者当前的drilldown深度
已经大于设置的drilldown深度那么就跳出处理过程。
if(iscircularref(locprop)) continue;
创建一个能包含用于这个collection中当前的局部引用持久性与关系信
息的元素,然后把它添加到表现这个collection的元素中。
element grandchild =
doc.createelement(propname +
"-child");

child.appendchild(grandchild);

populateelement(grandchild, locprop);

}
parent.appendchild(child);

}catch(classcastexception ex2) {

如果抛出一个classcastexception的话,属性是一个持久性域并且要把它
的值添加为当前节点的值。
parent.setattribute(propname, prop.tostring());
}

}

}
从列表中移走当前的局部引用。
_circularref.remove(local);
递减当前的drilldown深度。
_currentdepth--;

}
这是一个公用程序方法,用来检查一个引用是否已经在连接引用列表
中。
private boolean iscircularref(ejblocalobject local) {

iterator it = _circularref.iterator();
while(it.hasnext())
if(local.isidentical((ejblocalobject)it.next())) return true;
return false;

}

}

  下面的代码片断演示如何使用这个类:

user local = userhome.findbyprimarykey("1");
return new domgenerator("user", 4).getdom(local);
下面演示的这段生成的dom取决于你的实体模型∶
<?xml version="1.0" encoding="utf-8"?>
<user firstname="wayne" id="1" lastname="zheng">

<requests>
quests-child description="outlook not working" id="2"
status="p">
<product description="install laptop" id="1"
name="svc01"/>
</requests-child>
<requests-child description="pc not booting" id="1" status="o">
<histories>
<histories-child description="informtion requested"
id="2"
loggedat="2002-4-7 12:12:12.0"/>
<histories-child description="request logged" id="1"
loggedat="2000-4-7 00:00:00.0"/>
</histories>
<product description="install laptop" id="1"
name="svc01"/>
</requests-child>
</requests>

<phones>
<phones-child id="2" number="0771 8210586" type="m"/>
<phones-child id="1" number="01908 251575" type="w"/>
</phones>

</user>

  在本文中,我们详细研究了一个模型用于在一个j2ee应用程序的企层和表现层之间传递复杂数据。表现层可以交付xml数据,这些数据在企业层上生成的,被使用xslt或者xml jsp自定义标记交付到客设备上。

  当你添加新的定义到你的域模型中的时候,你唯一需要做的事情就是使用精细局部实体bean定义,然后使用容器管理关系定义关系。dom生成程序提供一个一般的方法生成动态的xml文件。

  然而,就象任何其他的设计方案,这个方法也有它的缺点。一个明显的方面就是开发人员经常担心基于xml的数值对象的性能。在一台1ghz cpu, 256mb内存,操作系统为windows me的机器中,运行应用软件服务器(wls 6.1)和数据库服务器,上述对一千条纪录的记录集的操作以及处理多元联系用了不到0.5秒钟。第二个明显的方面就是类型安全性。因为xml提供了数据抽象化的最高级别,属性的结点值和文本节点总是被当做字符串。如果你打算在从企业层上检索回来的数值对象上进行进一步的事务操作的话,最好就使用java bean组件。然而,xml模式提供了一个功能强大的机制,用于加强xml文件的类型安全性。在两种情况下都要使用局部实体对象作为域对象。

  这个方法最大的缺点就是没有模型化基于xml数据的明确定义的方法。我还没有看见过任何模型化使用xml的域对象关系的uml注释或者通用的规范定义。



扫描关注微信公众号