IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  XML  >

关系数据库中的可选 XML,第 1 部分: 需要 null 值吗?

在数据库中规划 XML 元素并忽略 null 值

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

英文原文

英文原文


级别: 中级

Stephen B Morris, CTO, Omey Communications

2009 年 7 月 06 日

可选 XML 元素(比如在线表单)非常特别,因为它们可以选择性地出现在 XML 文件中。在编程语言和数据库中表示这类可选元素是件困难的事情。在很多情况下,通过向数据库插入 null 值来表示空的可选元素,但是要检索数据库中的 null XML 值会非常困难。本文发现一种处理可选 XML 元素的便捷方法,它不需要进行数据重载或插入 null 或其他标记值。

随着存储设备的性价比不断升高,人们再也不需要删除数据了。随之而来的是不断增长的数据量,并且,由于没有任何技术是真正免费的(包括开源技术),因此出现了数据管理需求。数据量激增问题目前刚刚开始影响主流用户,但对大型的数据服务提供组织而言,这个问题已经是存在多年的 “顽疾”。本文将探讨关系数据管理中的 XML 数据管理。

常用缩写词
  • JAR:Java Archive
  • XML:可扩展标记语言
  • XSD:XML Schema Infoset Model

数据管理涉及多种领域的专门技术,比如备份和恢复、数据库设计、服务提供商选择。备份应当每天执行吗?应当采取哪些方法来进行数据库设计?应当使用自然键还是应用程序生成的主键?对于服务提供商来说,应当对数据执行本地存储还是远程存储?

尽管这些问题都非常重要,但是您将探查一个非常具体的问题 — 这个问题会严重影响数据设计、数据检索和客户机代码管理。这个问题就是在关系数据库中表示可选 XML 元素。您将看到,针对可选 XML 元素建模而采用的解决方案将产生深远的影响。

路线图

本文将介绍许多种技术。为了清晰地展示主题,下面列出了我们将要讨论的内容:

  • XSD 和 XML 的简要回顾
  • 通过 Java Architecture for XML Binding (JAXB) 建立起 XML 和 Java™ 的联系
  • 发布 Java 类数据的简单引擎
  • 数据库实体类
  • 解决可选 XML 数据的问题

首先从一些数据定义开始。

XSD 中可选的 XML 元素

清单 1 提供了来自 XSD 文档的一个片段,其中指定了一个名为 comment 的可选因素。这个 XSD 文档定义了一个购订单。通常称为业务对象,您可以将这类订单看作应用程序域中的一个重要实体。换句话说,系统用户通常需要与这类业务对象进行交互 — 从而创建订单、修改现有订单,等等。重点在于 清单 1 描述了建模典型业务对象的起点。


清单 1. 包含一个可选元素的 XSD 类型定义
				
<xsd:complexType name="PurchaseOrderType">
  <xsd:sequence>
    <xsd:element name="shipTo" type="EuropeanAddress"/>
    <xsd:element name="billTo" type="EuropeanAddress"/>
    <xsd:element ref="comment" minOccurs="0"/>
    <xsd:element name="items" type="Items"/>
  </xsd:sequence>
  <xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>

在 XSD 上下文中,什么是可选 XML 元素?可选元素是指可以在相关 XML 文档中出现 0 次或出现多次的元素。不要考虑 XSD 和 XML 文档之间的关系:XSD 只不过是一种格式,允许您定义 XML 文档中出现的结构和类型。实际上,清单 1 中的每个 xsd:element 都为 XML 文档中的一个项定义了一个占位符。现在我们将通过了解另一种技术 JAXB 来更加详细地解释这一点。

JAXB:连接 XML 和 Java 技术之间的桥梁

将 XML 描述为现代计算技术的通用语言并不过分。但是,就其本身来讲,XML 过于庞大并且并不具备良好的可读性。因此,如果能够将 XML 转换成 Java 代码岂不是很好?幸运的是,您可以使用好几种方法来实现这一点,我们这里就将介绍其中一种:JAXB。

JAXB 通过结合 XSD 文件和一个样例 XML 文件来生成 Java 类。生成的 Java 类随后表示 XSD 元素。听上去很困难?实际上并非如此,本文后面几节将查看一个完整转换的例子。如果希望跟随后面描述的步骤,那么需要安装 Sun Microsystems Java Web Services Developer Pack(参见 参考资料)。我使用的是版本 2.0。

遵照安装说明,然后特别注意如何创建名为 %JAVA_HOME%\jre\lib\endorsed 的目录并将 JAR 文件复制到该位置。这一步骤用来重写内置到 Java 软件开发工具箱(JDK)版本 1.5.x 中的 JAXB 类。

将 XML 转换成 Java 代码

清单 2 演示了一个 XML 文件,您可以将其视为 清单 1 中的 XSD 文档的一个实例。


清单 2. 示例 XML 文档
				
<?xml version="1.0"?>
<purchaseOrder orderDate="1999-10-20">
    <shipTo country="IE">
        <name>Alice Smith</name>
        <street>123 Maple Street</street>
        <city>Cambridge</city>
        <postcode>12345</postcode>
    </shipTo>
    <billTo country="IE">
        <name>Robert Smith</name>
        <street>8 Oak Avenue</street>
        <city>Cambridge</city>
        <postcode>12345</postcode>
    </billTo>
    <items>
        <item partNum="242-NO" >
            <productName>Nosferatu - Special Edition (1929)</productName>
            <quantity>5</quantity>
            <USPrice>19.99</USPrice>
        </item>
        <item partNum="242-MU" >
            <productName>The Mummy (1959)</productName>
            <quantity>3</quantity>
            <USPrice>19.98</USPrice>
        </item>
        <item partNum="242-GZ" >
            <productName>Godzilla and Mothra: Battle for Earth
/Godzilla versus 
                    King Ghidora</productName>
            <quantity>3</quantity>
            <USPrice>27.95</USPrice>
        </item>
    </items>
</purchaseOrder>

那么,问题就变得非常简单:您需要将 清单 2 的内容转换为一组相关 Java 类。使用 JAXB 和 Apache Ant 可以很容易地完成这个过程:

ant compile -Djwsdp.home=C:\Sun\jwsdp-2.0

这个命令创建了一组新的文件夹和 Java 类文件。Java 类文件与 XSD 文件中的元素相关。现在应该开始让这些类投入工作了。

发布 Java 类数据的简单引擎

现在需要将生成的类集中到一个简单的程序中。清单 3 解释了程序的关键部分,这个程序来自于 Java Web Services Developer Pack 附带的一个示例并进行了修改。


清单 3. JAXB 程序
				
JAXBElement<?> poElement =
   (JAXBElement<?>)u.unmarshal(new FileInputStream("po.xml"));
   PurchaseOrderType po = (PurchaseOrderType)poElement.getValue();

   // examine some of the content in the PurchaseOrder
   System.out.println("Ship the following items to: ");

  // display the shipping address
  EuropeanAddress address = po.getShipTo();
  displayAddress(address);

  // display the items
  Items items = po.getItems();
  displayItems(items);

清单 3 中,您将名为 po.xml 的 XML 文件解组为 PurchaseOrderType 类的一个实例。然后深入这个类并提取构成元素。所有操作均由 JAXB 生成的类完成。创建好自动生成的类后,可以按照下面的方式使用它们:

ant run -Djwsdp.home=C:\Sun\jwsdp-2.0

清单 4 展示了这一命令的程序输出。


清单 4. 转换和程序输出
				
ant run -Djwsdp.home=C:\Sun\jwsdp-2.0

compile:
     [echo] Compiling the schema...
      [xjc] C:\java\ibmcode\unmarshal-read\gen-src\primer.po is not found and thus
excluded from the dependency check
      [xjc] Compiling file:/C:/java/ibmcode/unmarshal-read/po.xsd
      [xjc] Writing output to C:\java\ibmcode\unmarshal-read\gen-src
     [echo] Compiling the java source files...
    [javac] Compiling 4 source files to C:\java\ibmcode\unmarshal-read\classes

run:
     [echo] Running the sample application...
     [java] Ship the following items to:
     [java]     Alice Smith
     [java]     123 Maple Street
     [java]     Cambridge null
     [java]     IE
     [java]
     [java]     5 copies of "Nosferatu - Special Edition (1929)"
     [java]     3 copies of "The Mummy (1959)"
     [java]     3 copies of "Godzilla and Mothra: Battle for Earth/Godzilla versus
King Ghidora"

BUILD SUCCESSFUL
Total time: 9 seconds

注意 清单 4 如何利用 JAXB 重新生成 清单 2 中的数据。

您已经使用一对 XSD 和 XML 文件表示了域数据。您结合使用了 XSD 文件和 JAXB 创建了一套相关 Java 类。然后使用了一个简单 Java 程序并结合 XML 文件来处理生成的类,并显示 XML 文件的内容。接下来需要做什么?

回忆一下同时生成可选 XML 数据的 XSD 文件。路线图的下一步就是将 XML 数据持久化到一个关系数据库中。这听起来有些困难,但是通过使用 Java Persistence 应用程序编程接口(JPA)和 Hibernate,这个任务将变得相当简单。

数据库实体类

清单 5 解释了名为 PurchaseOrder 的新 Java 类的最重要的部分。


清单 5. PurchaseOrder Java 类
				
    @Id @GeneratedValue
    @Column(name = "PO_ID")
    private Long id;

    @Embedded
    private ShippingAddress shippingAddress;
    @Embedded
    private BillingAddress billingAddress;

    @Column(name = "COMMENT")
    private String comment;
    @Column(name="COMMENT_ENTERED", columnDefinition="boolean default false")
    private boolean commentEntered;

清单 5 中,您有一个称为 PO_ID 的标准主键列(即订单 ID)。该列后跟两个地址:一个用于送货,一个用于付款。接下来的部分比较有趣:COMMENT 数据库列,它代表可选的 XML 元素。

注意 清单 5 中的最后一列:COMMENT_ENTERED 列。这是一个布尔类型的列,根据以下条件反映用户的操作:

  • 如果用户输入一个注释,那么 COMMENT_ENTERED 为真。
  • 如果用户没有输入注释,那么 COMMENT_ENTERED 为假。

这实际上解决了最初的可选 XML 元素的问题!这种方法有什么特别的?要回答这个问题,您需要对关系数据库中的 null 值有一定的了解。

不要使用 null 列

通常,一种优秀的实践就是不要在关系数据库中使用 null(即没有值)列,因为当您试图读取这类列时,您将得到一个 null 值异常。这又意味着您的 Java 异常代码必须捕捉这个异常。最终结果是生成了没有实际业务效益的复杂 Java 代码。

以上面的 COMMENT 列为例。如果没有任何注释与给定订单对象相关,那么您可能希望在持久化实体后将该列设为 null。这意味着您的 Java 异常代码必须预料到 null 值(以及相关异常)的可能性。实际上,这类 null 值是一种数据列重载,并且在这个上下文中,null 值可能被理解为表示数据的某些方面。事实上根本不需要以这种方式使用 null 值!如前所述,使用一个额外的布尔列就可以避免这个问题。

因此,在您的 Java 提取代码中读取 COMMENT 列之前,可以检查 COMMENT_ENTERED 列的值,并且只有在这时尝试读取。这样做有什么好处?最大的优点就是简单。数据定义被简化,因为它反映了用户和数据的交互。同样,Java 提取代码更加干净、更加迅速,并且不会抛出任何 null 值异常。

结束语

XML 是现代计算的通用语言。但是,在关系数据库中存取 XML 数据仍然有一些困难。一般的解决办法是使用其中一种 Java 编组技术,比如 JAXB,它允许以 XSD 文件的方式简洁地定义业务对象。在生成代码的同时,这些业务对象也被转换为 Java 类。这些类之后又被转换为适合进行持久化的实体。

那么,应该如何解决可选 XML 元素的问题?许多企业采取的一种办法是在相关数据库列中插入 null 值。这样做是否有利?是必须的吗?在关系数据库中插入 null 值是不良实践。对于遗留(即非 XML)数据是这样,对于 XML 数据也是如此。即使 XML 包含了可选因素,仍然可以避免使用 null 值。不管应用程序域有多么复杂,请使用可靠的设计实践来引导您做出决策。

与使用 null 值的方法不同,这里只需要定义额外的元数据来反映数据语义。因此,对于任何有可能存在 null 值的列,只需要定义一个额外的布尔列。这个额外的列可以拥有一个(假)默认值,这个值仅在 partner 列有值时才被修改,从而避免使用 null 值。



参考资料

学习

获得产品和技术

讨论


关于作者

Stephen Morris 是爱尔兰的一名独立作家/顾问。凭借在企业开发和网络应用程序方面的丰富经验,Stephen 曾在一些世界最大的网络公司参与过各种软件项目,包括基于 J2EE/J2SE 的网络管理系统,帐单编制应用程序、财务系统、移植和开发 SNMP 实体、网络设备技术和一些移动计算应用程序。他拥有计算机科学的硕士学位,并且拥有三项网络管理方面的专利。他是 Moving Your Career Up the Value Chain: Building Specialized Development Skills in a Global Economy 一书的作者,并且发表过多篇有关网络管理和其他主题的文章。




对本文的评价










回页首


IBM、IBM 徽标、ibm.com、DB2、developerWorks、Lotus、Rational、Tivoli 和 WebSphere 是 International Business Machines Corporation 在美国和/或其他国家的商标或注册商标。这些和其他 IBM 商标术语第一次出现时,会用合适的符号(® 或 ™)进行标记,表示这些美国注册商标或普通法商标在发布本信息时归 IBM 所有。这些商标也可能是其他国家的注册商标或普通法商标。请查阅最新的 IBM 商标 列表。 Adobe、Adobe 徽标、PostScript 和 PostScript 徽标是 Adobe Systems Incorporated 在美国和/或其他国家的注册商标或商标。 Java 和所有基于 Java 的商标是 Sun Microsystems, Inc. 在美国和/或其他国家的商标。 其他公司、产品或服务名称可能是其他公司的商标或服务标志。 其他公司、产品或服务的名称可能是其他公司的商标或服务标志。

IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款