技术开发 频道

使用DB2 pureXML实现动态模型定制

如何使用 DB2 pureXML 管理模型

    前面谈到了 XML 可以用来简便的描述模型,也谈到了使用 Schema 来描述 XML 模型结构的便利性。这些模型以及模型结构可以分别以 XML 形式和 XML Schema 形式存储到数据库中,并有效的组织管理起来。由于 XML Schema 本身也是 XML,因此,它们均可保存在 DB2 的 XML 字段中。

    下面是采用 DB2 管理模型的一个架构示意图。

DB2 管理模型架构示意图
图 1. DB2 管理模型架构示意图

 
-开发商根据最初采集到的需求预定义若干模型,并将这些模型定义存储在 DB2 pureXML 中;
-随着业务的发展,最终用户根据自身的需要来定义模型,并将这些模型定义存储在 DB2 pureXML 中;
-程序从 DB2 数据库中获取出来模型定义,并解析这些模型,提供出来可供最终用户录入模型数据的视图,以及修改模型数据的视图,并根据模型定义的约束进行输入有效性验证;
-用户通过该视图提交表单,程序将解析表单的输入内容,并将其组装成为符合模型定义的 XML 文档,从而存储到 DB2 数据库中;
-程序读取出来用户存储的模型,展现到视图上。

    从开发商的角度来讲,由于专注在软件开发领域,不可能完全洞悉客户的所有需求,在建模上只需要根据最初和客户洽谈下来的需求定义若干预定义模型,然后提交给客户进行维护,避免了因为客户需求变更或者需求理解偏差而产生的大量修改工作。从而避免了很大的风险。当产品成型之后,事实上,对不同客户的开发工作转移到不同的模型的定制上来了。

    反过来,从客户的角度来讲,业务是掌握在自己的手中,碰到业务的扩展,制度的变更等再也不需要请开发商来重新开发,或者修改原有系统。只需要自己定义新的模型,或者扩展原有的模型,就可以满足新的需求。从而最大程度的减少了成本。 

    在具体的实现层面,关键点在于:

-为最终用户提供友好的模型定制视图;
-根据模型定义展现视图,使得用户可以输入,并辅助进行校验;
-读取用户输入,并组装成为 XML,并存入 DB2。 

    下面以 Web 方式提供一个实现的个案,开发者可以根据自身的条件和需求,以任何方式实现,实现的方法都是相通的。

为最终用户提供友好的模型定制视图 

    为了定义模型,模型元素需要可以随意的扩展增加,并可随意添加子元素。由于模型定义最终存储为 DB2 中的 XML Schema,程序中只需要借助 DB2 pureXML 的 xmlquery() 函数读取 DB2 中 XML Schema 的根节点下的优异子节点,并以表格的形式展现出来。

清单 3. 读取 XML Schema 并以表展现出来

SELECT t.*,xmlquery('declare namespace xsd="http://www.w3.org/2001/XMLSchema";
$doc/xsd:schema/xsd:complexType/xsd:sequence/xsd:element'
passing schema_info as "doc")
as schema_name FROM template t where record_id=? 


    用户就可以对该表格的各行(即各元素)进行重新命名,定义其元素类型,增加元素约束,添加新元素等。这些操作都可以通过 DB2 pureXML 的节点更新功能来完成目标。关于 XML 节点更新的语法,可以参考 参考资源。


清单 4. 通过更新 XML Schema 来更新模型 

update template set schema_info = xmlquery( 'declare namespace xsd="http://www.w3.org/2001/XMLSchema"; transform copy $new:=$i modify do insert <xsd:element name="newElement"> <xsd:simpleType> <xsd:restriction base="xsd:integer"> <xs:minInclusive value="0"/> <xs:maxInclusive value="100"/> </xsd:restriction> </xsd:simpleType> </xsd:element> as last into $new/xsd:schema/xsd:element/xsd:complexType/xsd:sequence return $new' passing schema_info as "i") where XMLEXISTS('declare namespace xsd="http://www.w3.org/2001/XMLSchema";$doc/xsd:schema/xsd:element[@name=$parentName]' PASSING schema_info AS "doc", “parentName” as $parentName);

    用户也可以通过点击某行,进入该元素的下一个级子元素列表,定义下一级子元素的模型,从而递归进行操作,达到模型定制的基本要求。

根据模型定义展现视图获取用户输入并进行校验 

    由于 XML 模型本身的结构是动态的,因此在展现该模型的时候也应该采用动态的方法才能满足要求。 

    这里使用了 XSLT 来处理最通用的情形,当然,开发者可以根据自身的模型特点,编写不同的 XSLT 转换样式,使得展现的更加人性化。除了可以利用 Java API javax.xml.transform.Transformer 类的 transform() 方法来实现 XSLT 转换之外,DB2 pureXML 也提供了转换函数 XSLTransform(xmldoc USING xsltdoc),方便用户使用。 

    该样式表取得 /xsd:schema/xsd:element/xsd:complexType/xsd:sequence/xsd:element 列表,并递归应用样式,对不同的约束条件,展现不同的输入界面。 

    如对于 <xsd:enumeration> 的约束限制,则显示为下拉框,供最终用户选择输入; 

    对于 <xsd:minInclusive>,<xsd:maxInclusive> 等,则在 <input> 元素增加属性 t_min,t_max 等,并在提交表单时检查这些属性和输入值的关系,验证输入值是否满足约束。

清单 5. XSL 示例 

<?xml version="1.0" encoding="gb2312"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsl:output method="html" encoding="gb2312" /> <xsl:template match="/"> <xsl:apply-templates select="/xsd:schema/xsd:element/xsd:complexType/xsd:sequence/xsd:element"> <xsl:with-param name="level" select="'1'" /> </xsl:apply-templates> </xsl:template> <xsl:template match="xsd:element"> <xsl:param name="level" /> <table border='0' width="100%" id="faqLev1" style="background:#FFFFFF;margin-left:16px;float:none; margin-bottom:10px;DISPLAY: block;"> <TR><TD height="25"> <div id="openlev" style="cursor:hand;display: block" onclick="javascript:toggleLev2Div(this)">+Toggle</div> </TD></TR> <tr style="DISPLAY: none"> <td width="100%"> <b><xsl:value-of select="@name" /></b> <xsl:for-each select="."> <xsl:choose> <xsl:when test="xsd:restriction"> <xsl:for-each select="./xsd:restriction"> <xsl:choose select="."> <xsl:when test="xsd:enumeration"> <xsl:element name="select"> <xsl:attribute name="name"> <xsl:value-of select="../@name" /> </xsl:attribute> <xsl:attribute name="t_level"> <xsl:value-of select="$level" /> </xsl:attribute> <xsl:for-each select="./xsd:enumeration"> <xsl:element name="option"> <xsl:attribute name="value"> <xsl:value-of select="@value" /> </xsl:attribute> <xsl:value-of select="@value" /> </xsl:element> </xsl:for-each> </xsl:element> </xsl:when> <xsl:otherwise> <input type="text" value=""> <xsl:attribute name="id"> <xsl:value-of select="../@name" /> </xsl:attribute> <xsl:attribute name="name"> <xsl:value-of select="../@name" /> </xsl:attribute> <xsl:attribute name="t_level"> <xsl:value-of select="$level" /> </xsl:attribute> <xsl:if test="xsd:minInclusive"> <xsl:attribute name="t_min"> <xsl:value-of select="xsd:minInclusive/@value" /> </xsl:attribute> </xsl:if> <xsl:if test="xsd:maxInclusive"> <xsl:attribute name="t_max"> <xsl:value-of select="xsd:maxInclusive/@value" /> </xsl:attribute> </xsl:if> </input> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:when> <xsl:otherwise> <input type="text" value=""> <xsl:attribute name="id"> <xsl:value-of select="./@name" /> </xsl:attribute> <xsl:attribute name="name"> <xsl:value-of select="./@name" /> </xsl:attribute> <xsl:attribute name="t_level"> <xsl:value-of select="$level" /> </xsl:attribute> </input> </xsl:otherwise> </xsl:choose> </xsl:for-each> <xsl:for-each select="@type"> <xsl:if test=".='xsd:string'">(Input String)</xsl:if> <xsl:if test=".='xsd:double'">(Input Numeric)</xsl:if> <xsl:if test=".='xsd:date'"> (Input date : yyyy-MM-dd) </xsl:if> </xsl:for-each> <xsl:apply-templates select="xsd:element"> <xsl:with-param name="level" select="$level +1" /> </xsl:apply-templates> </td> </tr> </table> </xsl:template> </xsl:stylesheet>

    关于 XSLT 的写法,可以参考文章。

将用户输入组装成 XML 模型并存储入库 

    由于模型的录入界面本身就是根据 Schema 的定义,按照定义的顺序依次排列出来模型元素的。同时,还为每个模型元素的录入元素设定了一个属性 t_level,因此,通过以下的 javascript 即可以实现将各个输入元素组装成为 XML。 

    在页面上,定义一个 <input name=”xmlarea” type=”hidden”>, 用以将组装的 XML 内容提交给表单。从而传递给后台应用程序进行处理,并入库。

清单 6. 组装 XML

function getXMLFragment(){ var stack = new Array(); var inputs=frm.elements; var lastElement=null; var xml=""; var element; for(var i=0;i<inputs.length;i++){ element = inputs[i]; if(element.t_level==undefined)continue; // 同级别的元素 , 将上个元素结尾 if(lastElement!=null && element.t_level==lastElement.t_level){ xml=xml+"</"+lastElement.name+">"; }else if(lastElement!=null && element.t_level>lastElement.t_level){ // 碰到子元素,将父元素压栈 stack.push(lastElement); }else if(lastElement!=null && element.t_level<lastElement.t_level){ // 碰到更高级元素,将同级别的父元素出栈 xml=xml+"</"+lastElement.name+">"; var startTag; do{ startTag=stack.pop(); if(startTag!=undefined){ xml=xml+"</"+startTag.name+">"; } }while(startTag!=undefined&& startTag.t_level<=lastElement.t_level); } xml=xml+"<"+element.name+" name=\""+element.name+"\" value=\""+element.value+"\">"; lastElement=element; } xml=xml+"</"+lastElement.name+">"; for(var i=stack.length-1;i>=0;i--){ xml=xml+"</"+stack[i].name+">"; } document.getElementById("xmlarea").value=xml; }
0
相关文章