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

developerWorks 中国  >  XML  >

关系数据库中的可选 XML,第 2 部分: 使用 JAXB 和 Java 注释创建、存储和操纵可选的 XML 数据

将 XML 定义的可选数据移入关系数据库

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码

英文原文

英文原文


级别: 中级

Stephen B Morris, CTO, Omey Communications

2009 年 8 月 13 日

本文探讨了创建、存储和操纵可选 XML 关系数据所需的软件 — 是这份共 2 部分的系列文章的第 2 部分。本文提供了完全可以运行的代码示例,使用的软件包括 Java™ Architecture for XML Binding (JAXB)、Java Persistence API (JPA)/Hibernate、内存数据库、持久性相关数据库。
本系列的其他文章

在本系列的 关系数据库中的可选 XML,第 1 部分:需要 null 值吗? 中,您了解了如何使用 JAXB 将 XML 域转换为 Java 代码。这种域间转换的概念在很多领域的学习中都很有用 — 例如,信号处理领域中从时间域移入频率域。一个相关的例子是 XSLT,其中样式表用于将 XML 转换为其他文本格式,比如 HTML。现在,来看一下 JAXB 所需的设置。

设置 JAXB

通常,我不会过多地介绍软件安装,但是对于 Java Web Services Developer Pack 而言(请参见 参考资料),安装比通常情况下要复杂一些。因此,我会介绍一下这个过程,帮您顺利完成安装,使其正常运行。

首先,要 下载该软件包的版本 2.0。Web 服务下载相对较小 —23MB,不会花太长时间。如果在读过本文后还想了解更多内容,可以下载 Web 服务文档包(可在同一页面上找到)。要运行 InstallShield 向导,双击下载的可执行文件,然后按照说明操作。我建议不要安装 Web 容器。另外,我使用 C:\Sun\jwsdp-2.0 作为安装目录。

常用缩写词
  • API:应用程序编程接口(Application programming interface)
  • HTML:超文本标记语言(Hypertext Markup Language)
  • SQL:结构化查询语言(Structured Query Language)
  • XML:可扩展标记语言(Extensible Markup Language)
  • XSD:XML 模式定义(XML Schema Definition)
  • XSLT:可扩展样式表语言转换(Extensible Stylesheet Language Transformation)

安装好下载的软件后只有一件最重要的事要做:在 %JAVA_HOME%\jre\lib 目录内,创建名为 endorsed 的文件夹。然后,将 C:\Sun\jwsdp-2.0\lib 文件夹的内容粘贴到新 文件夹 %JAVA_HOME%\jre\lib\endorsed 中。%JAVA_HOME% 环境变量 是到 Java 软件开发工具包 (JDK) 安装的完整路径。如果愿意,可以在命令行中使用 java.endorsed.dirs 系统目录,创建新目录是较为简单的一种方法。

还需要一个 Apache Ant 的副本(请参见 参考资料)。下载并安装 Ant,确保可执行程序位于系统路径中。

运行源代码

本文的源代码,作为 .zip 文件包含在 下载 中。要查看本文提供的示例,只需将文件内容解压到文件夹(比如 C:\article_code)中即可。在该目录下应该看到两个文件夹:一个叫做 unmarshal-read,另一个叫做 dbcode。





回页首


运行一个 JAXB 示例

要确保正确配置了环境,在一个示例文件夹(比如,C:\Sun\jwsdp-2.0\jaxb\samples\unmarshal-read)中打开 DOS 控制台。尝试在该文件夹中运行 Ant 目标,如 清单 1 所示。


清单 1. 运行 Ant
				
C:\Sun\jwsdp-2.0\jaxb\samples\unmarshal-read>ant -p
Buildfile: build.xml
This sample application demonstrates how to unmarshal 
an instance document into a Java content tree and access 
data contained within it.
			
Main targets:
clean    Deletes all the generated artifacts.
compile  Compile all Java source files
javadoc  Generates javadoc
run      Run the sample app
Default target: run

看到程序输出如 清单 1 所示时,就可以确定安装成功了。在传递过程中,注意 ant -p 命令可以很方便的确定某个 build.xml 文件支持的目标。它使您能在不运行任何目标的情况下,窥探到 build 脚本的内部。

要运行示例程序,只需键入 ant 命令,不需要参数。会看到程序输出如 清单 2 所示。


清单 2. 运行 JAXB 示例程序
				
C:\Sun\jwsdp-2.0\jaxb\samples\unmarshal-read>ant
Buildfile: build.xml
compile:
[echo] Compiling the schema...
[xjc] C:\Sun\jwsdp-2.0\jaxb\samples\unmarshal-read\gen-src\primer.po is not 
found and thus excluded from the dependency check [xjc] Compiling file:/C:/Sun/jwsdp-2.0/jaxb/samples/unmarshal-read/po.xsd [xjc] Writing output to C:\Sun\jwsdp-2.0\jaxb\samples\unmarshal-read\gen-src [echo] Compiling the java source files... [javac] Compiling 4 source files to C:\Sun\jwsdp-2.0\jaxb\samples\unmarshal-read\classes run: [echo] Running the sample application... [java] Ship the following items to: [java] Alice Smith [java] 123 Maple Street [java] Cambridge, MA 12345 [java] US [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 vs. King Ghidora" BUILD SUCCESSFUL Total time: 9 seconds

清单 2 展示了一个完整的 JAXB 程序。稍后会看到这种技术的另一个示例,用于将 XML 转换为 Java 代码。(这里需要解释一下术语。谈到将 XML 解组(unmarshalling)为 Java 代码:编组(marshall)指的是将 Java 代码转换为 XML,解组 就是将 XML 转换为 Java 代码)。现在,看一下包含可选元素的 XML 数据。





回页首


可选元素的 XSD 规范

清单 3 展示了 xsd:complexType 元素,您一定会感兴趣的。


清单 3. 带有可选元素的 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>

清单 3 的内容是 po.xsd 文件的一部分,您会看到它是一个 XML 模式。清单 4 展示了一个符合 po.xsd 模式的 XML 文件(名为 po.xml)。


清单 4. 示例 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 vs. King Ghidora</productName>
         <quantity>3</quantity>
         <USPrice>27.95</USPrice>
       </item>
   <>/items>
</purchaseOrder>

注意 清单 4 中的 XML 不包含任何注释字段。如 清单 3 中所示,注释字段是可选的。

现在要将 清单 4 中的 XML 数据解组为一个或多个 Java 类。为此,在源代码文件夹 unmarshal-read 中打开 DOS 控制台。然后,运行以下命令:

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

清单 5 展示了预期的输出结果。


清单 5. XML 到 Java 的转换
				
C:\article_code\unmarshal-read>ant compile -
Djwsdp.home=C:\Sun\jwsdp-2.0
Buildfile: build.xml

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

BUILD SUCCESSFUL
Total time: 5 seconds

清单 5 展示了 JAXB 的神奇。它从 XML 文件生成 Java 类并由清单 6 中展示的 Java 程序驱动。


清单 6. 驱动解组的代码
				
public static void main( String[] args ) {
        try {
            // create a JAXBContext capable of handling classes generated into
            // the primer.po package
            JAXBContext jc = JAXBContext.newInstance( "primer.po" );

            // create an Unmarshaller
            Unmarshaller u = jc.createUnmarshaller();

            // unmarshal a po instance document into a tree of Java content
            // objects composed of classes from the primer.po package.
            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 );

        } catch( JAXBException je ) {
            je.printStackTrace();
        } catch( IOException ioe ) {
            ioe.printStackTrace();
        }
    }

清单 6 中的代码由以下主要步骤组成:

  1. 创建 JAXB 上下文。
  2. 使用上下文创建解组器。
  3. 解组文件 po.xml。
  4. 创建 PurchaseOrderType 类的对象。
  5. 提取并显示来自对象的一些元素。

清单 6 的代码包含了众多功能。现在,您拥有了原始 XML 数据的 Java 表示。现在,要将这个数据推入关系数据库。





回页首


从 Java 代码转换为数据库实体

Java 注释的力量真正显现出来是在您希望持久化 Java 类的时候。这种从 Java 代码到关系域的映射叫做对象关系映射(ORM)。清单 7 提供了映射 ShippingAddress 类的示例。


清单 7. ShippingAddress 的 ORM
				
@Embeddable
public class ShippingAddress {

    @Column(name = "SHIPPING_ADDRESS_STREET")
    private String street;

    @Column(name = "SHIPPING_ADDRESS_CITY")
    private String city;

    ShippingAddress() {}

    public ShippingAddress(String street, String city) {
        this.street = street;
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    private void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    private void setCity(String city) {
        this.city = city;
    }
}

如果您看看 清单 7 并暂时忘记注释的事情,会发现它就是一个简单的 Java 类,叫做 ShippingAddress。事实上,使用集成开发环境 (IDE),比如 Eclipse,您可以轻松地自动生成 清单 7 的大部分代码 — 也就是构造函数以及 setter 和 getter 方法。注释只是用于将类及其属性映射到关系域。@Embeddable 注释表明这整个类都可以被嵌入到另一个类中(叫做实体类),如 清单 8 中所示。


清单 8. 实体类
				
@Entity
@Table(name = "PURCHASE_ORDERS")
public class PurchaseOrder {

    @Id @GeneratedValue
    @Column(name = "PO_ID")
    private Long id;

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

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

// More methods here
}

注意,清单 8 中,ShippingAddress 类实例被嵌入到 PurchaseOrder 类中的方式。这个实体类如何与数据库联系起来?





回页首


运行数据库代码

要运行数据库代码,在名为 dbcode 的文件夹中打开 DOS 控制台。通过键入 start 命令,创建两个额外的 DOS 控制台。在一个 DOS 控制台中,运行 Ant 目标 ant startdb

这一目标启动内存中的 HyperSQL DataBase (HSQLDB)。在该命令后,您会看到类似于 清单 9 中的输出。


清单 9. 运行 HSQLDB
				
C:\article_code\dbcode>ant startdb
Buildfile: build.xml

startdb:
     [java] [Server@1e0be38]: [Thread[main,5,main]]: checkRunning(false) 
entered
     [java] [Server@1e0be38]: [Thread[main,5,main]]: checkRunning(false) exited
     [java] [Server@1e0be38]: Startup sequence initiated from main() method
     [java] [Server@1e0be38]: Loaded properties from 
[C:\article_code\dbcode\server.properties]
     [java] [Server@1e0be38]: Initiating startup sequence...
     [java] [Server@1e0be38]: Server socket opened successfully in 62 ms.
     [java] [Server@1e0be38]: Database [index=0, id=0, db=file:database/db, 
alias=] opened successfully in 313 ms.
     [java] [Server@1e0be38]: Startup sequence completed in 375 ms.
     [java] [Server@1e0be38]: 2009-04-10 09:54:26.625 HSQLDB server 1.8.0 is 
online
     [java] [Server@1e0be38]: To close normally, connect and execute SHUTDOWN 
SQL
     [java] [Server@1e0be38]: From command line, use [Ctrl]+[C] to abort 
abruptly

清单 9 中运行 Ant 目标之后,就会有一个运行中的数据库管理器(目前还未运行数据库)。下一项任务是运行 schemaexport Ant 目标,这会创建数据库模式,如 清单 10 所示。


清单 10. 数据库创建
				
C:\article_code\dbcode>ant schemaexport
Buildfile: build.xml

compile:
    [mkdir] Created dir: C:\article_code\dbcode\build
    [javac] Compiling 4 source files to C:\article_code\dbcode\build

copymetafiles:
     [copy] Copying 2 files to C:\article_code\dbcode\build

schemaexport:
[hibernatetool] Executing Hibernate Tool with a JPA Configuration
[hibernatetool] 1. task: hbm2ddl (Generates database schema)
[hibernatetool]
[hibernatetool]     drop table PURCHASE_ORDERS if exists;
[hibernatetool]
[hibernatetool]     create table PURCHASE_ORDERS (
[hibernatetool]         PO_ID bigint generated by default as identity (start 
with 1),
[hibernatetool]         SHIPPING_ADDRESS_STREET varchar(255),
[hibernatetool]         SHIPPING_ADDRESS_CITY varchar(255),
[hibernatetool]         BILLING_ADDRESS_STREET varchar(255),
[hibernatetool]         BILLING_ADDRESS_CITY varchar(255),
[hibernatetool]         COMMENT varchar(255),
[hibernatetool]         COMMENT_ENTERED boolean default false not null,
[hibernatetool]         primary key (PO_ID)
[hibernatetool]     );

BUILD SUCCESSFUL
Total time: 8 seconds

注意 清单 10 中的 SQL 语句 create table PURCHASE_ORDERS。这时,您就拥有了一个常驻数据库,可以通过 Ant run 目标向其中插入数据。因此,在第三个 DOS 控制台,执行 ant run 命令,如 清单 11 所示。


清单 11. 运行程序
				
C:\article_code\dbcode>ant run
Buildfile: build.xml

compile:

copymetafiles:

run:
     [java] Value of CommentEntered false
     [java] 1 purchase order(s) found:
     [java] Broad Street
     [java] Boston
     [java] Comment entered: true

BUILD SUCCESSFUL
Total time: 3 seconds

清单 11 指出数据库已成功填充。要查看数据库内容,运行 ant dbmanager 命令(从另一个 DOS 控制台)。您会看到打开一个窗口,如 图 1 所示。


图 1. 数据库管理器应用程序
数据库管理器应用程序的屏幕截图

图 1 是一个简单的图形数据库管理器应用程序,连接到了您刚才创建的数据库。要对数据库运行一个查询,单击 Command > SELECT,然后完成 SQL 语句:

SELECT * FROM PURCHASE_ORDERS

单击 Execute SQL。会看到类似 图 2 中的片段。


图 2. 填充的数据库
填充后的数据库屏幕截图,显示了两个条目,其中一个已被填充,另一个包含空的注释列

如果将 图 1 中的数据库列与 清单 6清单 7 相比较,会发现 Java 代码实际上已经被包含到了数据库中。这当然多亏了 ORM 技术。图 1 中的数据库现在包含两行;清单 12 展示了创建这两行的 Java 代码。


清单 12. 持久化两个对象
				
PurchaseOrder purchaseOrder = new PurchaseOrder();
purchaseOrder.setShippingAddress(new ShippingAddress("Broad Street", 
"Boston"));
purchaseOrder.setBillingAddress(new BillingAddress("Broad Street", "Boston"));
purchaseOrder.setComment("A COMMENT");
purchaseOrder.setCommentEntered(true);

PurchaseOrder purchaseOrder1 = new PurchaseOrder();
purchaseOrder1.setShippingAddress(new ShippingAddress("Broader Street", "New 
York"));
purchaseOrder1.setBillingAddress(new BillingAddress("Broader Street", "New 
York"));

em.persist(purchaseOrder);
em.persist(purchaseOrder1);

清单 12 中的代码实例化了 PurchaseOrder 类的两个对象。这些对象被填充,然后您通过调用 em.persist() 将它们持久化到数据库中。注意,对于 purchaseOrder 对象而言,您显示地插入了一个注释;但是对于 purchaseOrder1 而言,则没有注释。记住这些,在 图 2 中,注意 COMMENT 列的某一行有一个空的条目。但是,相应的 COMMENT_ENTERED 列却从不为空;它不是 True 就是 False

确定某一个行是否包含注释时,不再需要检查 COMMENT 列的 null 值。只要读取 COMMENT_ENTERED 列的值,如果该值为 True,则 COMMENT 列具有一个值。换句话说,这说明您正确建模和实现了可选 XML 数据。





回页首


结束语

将 XML 数据转换成关系数据似乎有点麻烦。从 XSD 和 XML 文件开始并使用 JAXB 将其转换为相应的 Java 类。然后,使用 ORM 技术填充数据库。总之要完成很多的工作。

显然,本文涉及的问题很小。但是如果扩展到一个一般规模的企业应用程序,通常会有很多 XML 对象。这些是业务域对象,在 XML 中定义它们就很有意义了。一方面,XML 定义允许非程序员进行对象定义。另一方面您可以随后使用 JAXB 将 XML 无缝地转换为 Java 实体。

一旦 XML 对象位于 Java 域中,注释的力量就会显现出来,允许您稍微修改一下 Java 类就可以使其成为持久实体。本文只是对将数据转换到关系数据库的一个简短介绍。

熟练使用 Java 对象关系注释有助于生成良好的模式定义。熟练 在这里意味着对注释元素的正确使用,如 nullable=false,仅允许被选定的项具有 “空值”。与 COLUMN_ENTERED 值一起使用,您就不用担心插入 null 值。这就是实践中的良好设计!






回页首


下载

描述名字大小下载方法
本文的源代码code.zip5700KBHTTP
关于下载方法的信息


参考资料

学习
  • 关系数据库中的可选 XML,第 1 部分:需要 null 值吗?(Stephen B. Morris,developerWorks,2009 年 6 月):计划使用可选的 XML 元素,跳过数据库中的 null 值,以侵入性较小的方式处理这些可能在 XML 文件中出现或不出现的可选的 XML 元素。

  • JPA:访问 Sun 的 JPA 维基,了解提供持久性的 Java Persistence API 和 Java EE 平台的对象/关系映射的更多信息。

  • IBM XML 认证:了解如何成为 IBM 认证的 XML 和相关技术的开发人员。

  • XML 技术库:访问 developerWorks XML 专区,获得广泛的技术文章和技巧、教程、标准和 IBM 红皮书。

  • 技术书店:浏览关于这些和其他技术主题的图书。

  • developerWorks 技术活动网络广播:随时关注技术的最新进展。

  • developerWorks podcasts:收听针对软件开发人员的有趣访谈和讨论。

获得产品和技术

讨论


关于作者

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 使用条款