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

developerWorks 中国  >  XML | Java technology  >

实现 Castor 数据绑定,第 4 部分: 把 Java 对象绑定到 SQL 数据库

使用 Castor 把 Java 对象绑定到 SQL 数据存储

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论

样例代码

英文原文

英文原文


级别: 高级

Brett McLaughlin (brett@newInstance.com), 作家兼编辑, O'Reilly Media Inc.

2008 年 4 月 30 日

通过前面的介绍,您已经了解到 Castor 实现 Java™ 对象和 XML 之间的转换是多么灵活。Castor 还有更多的功能,包括将这些 Java 对象转化成 SQL 数据库中的行。了解语法上有哪些相同的地方和不同的地方,将 SQL 纳入您的数据绑定工具库中。

对于多数开发人员 — 尤其是各种 Java 版本的用户 — 数据绑定已经和闭包、单例(singletons)、Ajax 一样变为常用词汇表中的一部分了。而且和其他术语一样,数据绑定也常常被错误的定义。

特别是大部分程序员在听到数据绑定 的时候,实际上想的是 XML 数据绑定。加上这个小小的单词 XML,造成大多数程序员忽略了相当多的功能和灵活性,尤其是如果使用 Castor API 的话。这是因为对于 Castor 而言,XML 数据绑定仅仅是其中的一部分。除了绑定到 XML 文档之外,Castor 还提供了将 Java 数据绑定到 SQL 数据库的能力。这就是所谓的 SQL 数据绑定

定义 SQL 数据绑定

经常用到的缩写词
  • Ajax:异步 JavaScript + XML
  • API:应用程序编程接口
  • IDE:集成开发环境
  • XML:可扩展标记语言

SQL 数据绑定 也许是一个新的术语,概念其实非常简单。实际上,最好从一个更熟悉的术语 XML 数据绑定 出发来考察。XML 数据绑定就是在 XML 文档数据 — 通常存储在元素和属性中 — 和 Java 对象模型的属性之间建立映射的过程。可以通过编组程序和解组程序在两者之间移动数据。编组程序从 Java 对象模型获得数据存储到 XML 文档中,解组程序从 XML 文档获取数据存入 Java 对象模型的属性中。

在这个基础上,我们说 SQL 数据绑定是在 SQL 数据库数据 — 存储在模式、表、列等中 — 和 Java 对象之间建立映射的过程就毫不奇怪了。编组和解组的过程一样,只不过转换是在 Java 对象和 SQL 而不是 XML 之间进行的。实际上,多数数据绑定文章中如果将 XML 替换为 SQL元素数据 更改为表记录,讨论的内容就变成了 SQL 数据绑定了。





回页首


SQL 数据绑定的意义

Java 技术刚诞生的时候基本上是一种玩具语言,很大程度上是因为它的 API 非常简单,而且主要关注于图像(还记得 AWT 吗?)。Java 技术走向成熟的标志是 Java 数据库连接(JDBC),能够持久到 SQL 数据库。JDBC 过去(现在也是)惟一的问题是用起来很笨重。并不是说多复杂,而是对于大多数程序而言增加了太多的额外工作。

使用 Castor 和 SQL 数据绑定可以避免大部分复杂性。更妙的是,这种 API 对于 XML 和 SQL 上下文功能基本相同。而且使用数据绑定,应用程序需要关心的细节也少了。处理 JDBC ResultSet 和行数不再是您的代码的问题了,几次简单的编组解组调用就能处理 Java 对象和 SQL 数据库之间的转换。

本系列中的其他文章

关于 SQL 数据绑定可能最有意思的是它没有受到过多 的压力和关注。特别是考虑到一大批程序员对 XML 心存芥蒂,说它冗长笨拙,或者他们更愿意使用二进制序列化。但是这些人对 SQL 双手欢迎。事实上,如果发现有一大批程序员甚至真正的程序员也不认为 SQL 是一种合法的数据存储和持久技术,您可能会非常吃惊(看看你周围有多少人认为关系数据库臃肿笨拙、夸大其词或者已经落伍)。

既然如此,SQL 数据绑定和它的 XML 兄弟相比无疑更有意义。企业应用程序中必然需要持久(或存储)数据,而编写数据访问和检索代码是痛苦的。SQL 数据绑定把一种熟悉的 API(假设您已经阅读过本系列前面的文章)用于 SQL 数据存储。几行命令就能把数据写入表(或者多个表)或者读出来。

仍然是映射

SQL 数据绑定特别重要的一点是,能够建立 Java 对象到 SQL 数据模式的映射而不需要绑定 Java 或者 SQL 名称。和 XML 相比,关系数据库的结构通常和 Java 对象的结构相距甚远。表是数据的集合,对象通常代表一部分(可能是一行)数据。对象之间的关系必须跟踪到其他对象,就像表之间的关系要跟踪到其他表。但是 Java 对象模型中没有一对多的连接表,当然也没有多对多连接。

即便是中等复杂程度的关系数据库,设计的东西也和对象模型不同。SQL 和 Java 对象之间的映射,大量的工作就是定义对象和表之间的映射。虽然映射可能很复杂,本文中将使用几个简单的映射说明 SQL 数据绑定中映射的基本原理。





回页首


这就是 JDO,对吗?

是。但也不全对。有几分对。这里有点让人费解。

标准真的很重要吗?

一些开发人员 — 或者他们的老板 — 认为使用标准 API 是技术选择中惟一最重要的因素。其他人可能不那么重视。一般来说,最佳选择是采用对您的工作而言效率最高的 API,不论是不是 “标准” API。提高效率重于使用非标准 API 带来的所谓副作用。

就是说,看看非标准 API 的历史仍然是一种不错的办法。比方说,如果发现一个很好的工具只存在了三个月,是某个家伙在车库里鼓捣出来的,后面可能要面临着升级和支持问题(即便他可能会成为下一个 Bill Gates 或者 Steve Jobs)。如果想选择一个不那么短命的 API,只要看看开发历史是否足够长(2.0 或者更高版本号就是情况良好的信号),最好有一些文档或者在线支持。

Sun 有一个规范叫 Java 数据对象(Java Data Objects,JDO)。Java Specification Request (JSR) 12(JDO 1.0)和 JSR 243(JDO 2.0)定义了一种非常具体的 SQL 数据绑定方法(虽然从未明确地称为 “SQL 数据绑定”)。如果读过 JDO 然后再读本文开始的介绍部分,就会发现是一件事。但是,Castor 的 JDO(不错,Castor 也叫 JDO,事情更加混乱) 同于 Sun 的 JDO,甚至没有任何关系,只不过两者的基本目标相同而已。

鉴于可能造成很大混淆,再强调一次:如果使用 Castor 的 JDO,那么它不是 Sun 的标准 API。但问题并不像看起来这么糟糕或者简单明了。实际上,Castor 的开发者正努力将其 API 的很多特性纳入 Sun 数据绑定和 JDO API。因此,虽然 Castor 现在还不是标准 API,但很多特性 — 包括 API 自身 — 可能有一天会成为标准化的 JDO API。

建立实验环境

在 XML 数据绑定中,两个端点分别是 Java 对象模型和 XML 文档(实际上是约束文档的 XML 模式)。对于 SQL 数据绑定来说,仍然有 Java 对象模型,不过另一端变成了 SQL 模式:一个或多个表以及表列。本文的例子使用了一组相对比较简单的端点。

本系列文章一直使用 Java 对象模型表示图书和作者,本文仍然使用同样的数据模型。清单 1 显示了 Book 类的代码。


清单 1. Book 类
                
package ibm.xml.castor;

import java.util.LinkedList;
import java.util.List;

public class Book {

 /** The book's ISBN */
 private String isbn;
 /** The book's title */
 private String title;
 /** The authors' names */
 private List<Author> authors;

 public Book() { }

 public Book(String isbn, String title, List<Author> authors) {
 this.isbn = isbn;
 this.title = title;
 this.authors = authors;
 }

 public Book(String isbn, String title, Author author) {
 this.isbn = isbn;
 this.title = title;
 this.authors = new LinkedList<Author>();
 authors.add(author);
 }

 public void setIsbn(String isbn) {
 this.isbn = isbn;
 }

 public String getIsbn() {
 return isbn;
 }

 public void setTitle(String title) {
 this.title = title;
 }

 public String getTitle() {
 return title;
 }

 public void setAuthors(List<Author> authors) {
 this.authors = authors;
 }

 public List<Author> getAuthors() {
 return authors;
 }

 public void addAuthor(Author author) {
 authors.add(author);
 }
}

清单 2Author 类的代码。


清单 2. 作者类
                
package ibm.xml.castor;

public class Author {

 private String firstName, lastName;

 public Author() { }

 public Author(String firstName, String lastName) {
 this.firstName = firstName;
 this.lastName = lastName;
 }

 public void setFirstName(String firstName) {
 this.firstName = firstName;
 }

 public void setLastName(String lastName) {
 this.lastName = lastName;
 }

 public String getFirstName() {
 return firstName;
 }
 
 public String getLastName() {
 return lastName;
 }

}

前面的文章中我们建立了这些类的部分实例并持久到 XML 文档。前两期文章我们关注的是直接从对象模型转换到 XML 文档,两边使用相同的属性名。第三期文章突出 Java 对象和 XML 之间的来回转换,但是允许改变转换前后的命名约定。

这里我们需要取得类中的所有数据并将其存储到 SQL 数据库。不再是元素和属性,数据要放到列、行和表中。

建立 SQL 数据库模式

在某些方面,SQL 数据模式和 Java 对象模型的映射更密切,尤其是如果模型非常简单的话(比如这里的例子)。有两个表:dw_books 和 dw_authors。

实际上最简单的办法是先建立 dw_authors 表,因为 dw_books 表要引用它。该表包括三列:作者 ID、作者的姓氏和名字。如果按照本文操作的话,可以用 清单 3 中的代码在 MySQL 中建立该表。


清单 3. 作者表
                
CREATE TABLE 'dw_authors' (
'id' INT NOT NULL ,
'first_name' VARCHAR( 50 ) NOT NULL ,
'last_name' VARCHAR( 50 ) NOT NULL ,
PRIMARY KEY ( 'id' ) ,
INDEX ( 'first_name' , 'last_name' )
);

请注意,本文中所有的表都使用了 dw_ 前缀,以便于其他表区分开来,特别是 authorsbooks 都是很常见的表名。虽然可能有点罗嗦,但如果按照本文实验(希望如此)的话这样可以避免与系统中其他数据库的命名冲突。

接下来是 dw_books 表。这个表也很简单,在 SQL 中创建结构使用的数据定义语言(DDL)如 清单 4 所示。


清单 4. dw_books 表的 DDL
                
CREATE TABLE 'dw_books' (
'isbn' VARCHAR( 13 ) NOT NULL ,
'title' VARCHAR( 200 ) NOT NULL ,
PRIMARY KEY ( 'isbn' ) ,
INDEX ( 'title' )
);

最后还需要将这两个表联系起来。这就出现了一个问题。一本书可能有多位作者,就是说 dw_books 和 dw_authors 表之间存在多对多的关系。换句话说,一本书可以有多位作者,一位作者可以出版多本书。因此需要一个表连接图书和作者,而且这种连接必须是多对多的。

在 SQL 中这是很常见的:需要一个连接表,每行包括图书 ISBN 和作者 ID。清单 5 显示了表的结构。


清单 5. 作者和图书的连接表(多对多)
                
CREATE TABLE 'dw_books_authors' (
'book_isbn' VARCHAR( 13 ) NOT NULL ,
'author_id' INT NOT NULL ,
PRIMARY KEY ( 'book_isbn' , 'author_id' )
);

理解引用完整性

引用完整性 是一个美妙的 SQL 术语,它指的是保持数据库中的所有数据都是干净的,没有错误或者多余的数据,表之间的连接准确合理。对于这个例子来说,它意味着如果删除一本书,那么 dw_books_authors 表中关于该书 ISBN 的所有记录都将被删除。一旦这本书不存在了,它的 ISBN 也不应该有记录。对于作者来说情况一样,删除一位作者,关于其 ID 的所有记录也将被删除。

为此需要使用外键。因此,dw_books_authors 表中的 book_isbn 以 dw_books 表中的 isbn 作为外键,dw_books_authors 中的 author_id 和 dw_authors 中的 id 也存在同样的关系。这样就保证了表间的引用完整性。

不过多数数据库对引用完整性的处理方式不同,而且 MySQL 很多版本没有提供完整的支持。因此为了简化起见,本文没有使用外键。如果愿意创建外键,当然很好,请参阅数据库厂商提供的文档或者咨询数据库管理员(DBA)。





回页首


设置 SQL 数据绑定

虽然已经使用 Castor 进行 XML 数据绑定,为了能运行 SQL 数据绑定可能仍然需要稍微修改一下(至少增加一些)编程环境。

向类路径添加 Castor JDO 文件

首先需要把 Castor JDO 类放到类路径中。如果已经按照本系列第一期文章(链接参见 参考资料)的说明进行了安装,那么所有的 Castor Java ARchive (JAR) 文件已经在您的机器上了。但是,可能还需要将这些专用的 JDO JAR 文件添加到类路径中。在 Castor 目录下找到 castor-1.1.2.1-jdo.jar 并添加到类路径(您的版本也许更高,但意思一样)。

这是除了您可能已经放入类路径的其他 Castor JAR 文件之外的,比如 castor-1.1.2.1.jar、castor-1.1.2.1-xml.jar、Xerces 和 Commons Logging JAR 文件。如果使用应用服务器或者 IDE 运行代码,要确保适当修改类路径。

添加 JDBC 驱动程序

连接到 SQL 数据库需要 JDBC 驱动程序。用于 Oracle、MySQL、PostgreSQL 等的 JDBC 驱动程序都可免费获得。如果已经存储访问数据库的应用程序,可能已经足够了。否则,用 Google 搜索针对所用数据库的 JDBC 驱动程序,下载 JAR 并添加到类路径中。

对于本文使用的 MySQL 数据库,需要的 mysql-connector-java-3.0.17-ga-bin.jar 可从 MySQL 网站(详见 参考资料)免费下载。





回页首


将类映射到表

最简单的 SQL 数据绑定任务:把没有引用其他任何对象的对象映射到一个表。这是 SQL 数据绑定中最基本的操作,其他更高级的功能都是由此演变而来。

确定映射

首先将 Author 类映射到 dw_authors 表。需要的映射如表 1 所示。


表 1. Author 对象和 dw_authors 表之间的映射
Java 属性SQL 列
firstNamefirst_name
lastNamelast_name

太简单了,不过有一个突出的问题:dw_authors 表还有 id 列。事实上这是 dw_authors 表的主键,因此必须有。所以需要稍微修改 Author 类,如 清单 6 所示。


清单 6. 为 Author.java 增加 ID
                
package ibm.xml.castor;

public class Author {

 private String firstName, lastName;
 private int id;

 public Author() { }

 public Author(int id, String firstName, String lastName) {
 this.id = id;
 this.firstName = firstName;
 this.lastName = lastName;
 }

 public void setId(int id) {
 this.id = id;
 }

 public int getId() {
 return id;
 }

 public void setFirstName(String firstName) {
 this.firstName = firstName;
 }

 public void setLastName(String lastName) {
 this.lastName = lastName;
 }

 public String getFirstName() {
 return firstName;
 }
 
 public String getLastName() {
 return lastName;
 }

}

现在可以修改显示 Author 实例和 dw_authors 表行映射的表格了,如 表 2 所示。


表 2. 添加 ID 字段
Java 属性SQL 列
idid
firstNamefirst_name
lastNamelast_name

建立 Castor 映射

现在需要告诉 Castor 哪个 Java 属性映射到哪个 SQL 列。这里使用映射文件,类似于本系列第三期中用于 XML 的映射文件。清单 7 显示了用于将 Author 实例转化为 dw_authors 行的映射文件。


清单 7. 把 Author 对象映射到 dw_authors 表
                
<mapping>
  <class name="ibm.xml.castor.Author" identity="id">
    <map-to table="dw_authors" />
    <field name="id" type="int">
      <sql name="id" type="integer" />
    </field>
    <field name="firstName" type="string">
      <sql name="first_name" type="varchar" />
    </field>
    <field name="lastName" type="string">
      <sql name="last_name" type="varchar" />
    </field>
  </class>
</mapping>

没有什么新鲜之处,看起来和用于 Java 对于与 XML 的映射文件没有什么两样。将该文件保存为 sql-mapping.xml,现在告诉 Castor 如何将 Java 对象属性映射到数据库表。

标识符属性

与 Java/XML 映射相比,映射文件中惟一有变化的是使用了 identity 属性,用于标识 dw_authors 表的主键字段。以前可能没有使用过 identity 属性,因为 Castor 常常自己推断或者根本不需要。但是在 SQL 数据绑定中,该属性让 Castor 知道哪个字段惟一定义了一个对象;该例中是作者的 id 字段。

配置 Castor 进行 SQL 访问

在 XML 数据绑定中,我们已经介绍了编写代码把 XML 编组为 Java 对象以及反向操作,但是对于 SQL 数据绑定,还有一个软件需要应付:数据库自身。需要告诉 Castor 数据库在哪儿、如何登录以及使用哪个映射文件。这就需要用到配置文件了,通常命名为 jdo-conf.xml。清单 8 显示了可用于 MySQL 的配置文件。


清单 8. 为 Castor 定义 MySQL 连接方案
                
<?xml version="1.0" ?>
<!DOCTYPE jdo-conf PUBLIC "-//EXOLAB/Castor JDO Configuration DTD Version 1.0//EN"
 "http://castor.org/jdo-conf.dtd">
<jdo-conf>
 <database name="YOUR-DATABASE-NAME" engine="mysql">
 <driver class-name="com.mysql.jdbc.Driver"
 url="jdbc:mysql://YOUR-DATABASE-SERVER-HOSTNAME/YOUR-DATABASE-NAME">
 <param name="user" value="YOUR-DATABASE-USERNAME"/>
 <param name="password" value="YOUR-DATABASE-PASSWORD"/>
 </driver>
 <mapping href="sql-mapping.xml"/>
 </database>
 <transaction-demarcation mode="local"/>
</jdo-conf>

添加、查找和删除记录

现在已经做了很多设置工作,终于可以和数据库交互了。利用这些配置文件,需要加载配置、打开数据库然后访问它。清单 9 完成了这些工作,它创建了一个新的 Author 实例,保存到数据库,查找该实例然后删除。


清单 9. 对作者表使用 SQL 数据绑定
                
import ibm.xml.castor.Author;

import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.JDOManager;

public class SQLTester {
 public static void main(String args[]) {
 try {
 JDOManager.loadConfiguration("jdo-conf.xml");
 JDOManager jdoManager = JDOManager.createInstance("bmclaugh");

 Database database = jdoManager.getDatabase();
 database.begin();
 Author author = new Author(1001, "Joseph", "Conrad");
 database.create(author);

 Author lookup = (Author)database.load(Author.class,
 new Integer(1001));
 System.out.println("Located author is named " +
 author.getFirstName() + " " + author.getLastName());
 database.remove(lookup);
 database.commit();
 database.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}

我们来详细的观察一下每个步骤。

首先需要进行一些初始化工作,加载连接信息并连接到数据库。这个步骤基本上和任何使用 SQL 数据绑定的应用程序一样,因此只需要记住这些命令(或者把本文放入书签):

JDOManager.loadConfiguration("jdo-conf.xml");
JDOManager jdoManager = JDOManager.createInstance("bmclaugh");
Database database = jdoManager.getDatabase();
database.begin();

这里的目的是获得 Database 对象实例,它可用于创建、查找和删除记录。得到该实例后就可以创建新的 Author 并存储到数据库:

Author author = new Author(1001, "Joseph", "Conrad");
database.create(author);

调用 create() 的时候,Castor 将使用映射文件(sql-mapping.xml)来确定如何将传递给该方法的对象存储为 SQL 数据。数据进入数据库,然后就能访问了。

接下来根据标识符(这就用到了 identity 属性)查找对象:

Author lookup = (Author)database.load(Author.class,
 new Integer(1001));
System.out.println("Located author is named " +
 author.getFirstName() + " " + author.getLastName());

使用 load() 方法并传递需要加载的类类型以及记录的标识符值。load() 返回一个新实例,可以像其他 Java 对象使用,同样用到了 sql-mapping.xml 定义的映射。

这个例子删除了新添加的记录,保持数据库原来的状态:

database.remove(lookup);

最后还需要提交修改并关闭数据库:

database.commit();
database.close();





回页首


添加关系数据

可以看到,处理单一的类到表的映射并不难。但是一旦需要处理更加实际的情况,情况就复杂了,比如 BookAuthor 实例关联的 Java 对象模型。

定义基本的图书 SQL 映射

Author 一样,也需要在 sql-mapping.xml 中定义将 Book 类属性持久到关系数据库的映射。首先增加 清单 10 中的代码,它们用于处理图书的基本属性.


清单 10. 向映射文件添加图书信息
                
<mapping>
 <class name="ibm.xml.castor.Book" identity="isbn">
 <map-to table="dw_books"/>
 <field name="isbn" type="string">
 <sql name="isbn" type="varchar" />
 </field>
 <field name="title" type="string">
 <sql name="title" type="varchar" />
 </field>
 </class>

 <class name="ibm.xml.castor.Author" identity="id">
 <map-to table="dw_authors" />
 <field name="id" type="int">
 <sql name="id" type="integer" />
 </field>
 <field name="firstName" type="string">
 <sql name="first_name" type="varchar" />
 </field>
 <field name="lastName" type="string">
 <sql name="last_name" type="varchar" />
 </field>
 </class>
</mapping>

没有奇怪的地方,仅仅确定了 Book 和映射表(dw_books)以及如何映射对象的属性(isbntitle)。

定义多对多关系

现在情况复杂了:告诉 Castor 图书和作者之间的关系。要记住,关系是这样建立的:

  • 每本书都有惟一的 ISBN。
  • 每位作者都有惟一的 ID。
  • 如果某位作者写了一本书,在 dw_books_authors 建立一条记录包含图书 ISBN 和作者 ID。
  • 一本书可能有多位作者(dw_books_authors 多条记录的图书 ISBN 相同但作者 ID 不同)。
  • 一位作者可以写多本书(dw_books_authors 多条记录的作者 ID 相同但图书 ISBN 不同)。

首先,从 Book 类这一侧分析这种关系。对于一个 Book 实例,需要用该书的作者填充 authors 属性。反之亦然。如果 Book 实例的 authors 属性有数据,需要将这些作者通过 ID 存入数据库。必须映射该属性— authors —到 dw_books_authors 中的记录。

为此首先添加另一个 Castor field 元素,如 清单 11 所示。


清单 11. 使用 field 元素将 authors 映射为 Books 中的数组
                
<field name="authors" type="ibm.xml.castor.Author"
 collection="arraylist">
</field>

这很好理解,映射 authors 属性,但其类型是类而不再是 stringint。使用 collection="arraylist" 告诉 Castor 该属性是一个集合而不是单值。

但是现在需要说明该字段要存储作者 ID 以及该字段要存储到哪个表,因为作者 ID 在 dw_books 表中不存在。清单 12 给出了多对多表的名称 — 即 dw_books_authors — 以及该表中图书 ISBN 的键。


清单 12. 定义多表和多对多关系的 sql 元素
                
<field name="authors" type="ibm.xml.castor.Author"
 collection="arraylist">
 <sql name="author_id"
 many-table="dw_books_authors"
 many-key="book_isbn" />
</field>

小心地注意其中的逻辑。首先,要记住该元素用于 Book 实例的 authors 属性值。映射的实际字段是 Author 实例,需要找到该对象的标识符部分 — id — 并将其映射到列 author_id。但这不是在 dw_books 表中,因此使用 many-table 表明该表为 dw_books_authors。最后,many-key 属性表明要存储这个 Book 实例的 ISBN 到多对多表中。

清单 13 显示了包含这部分的完整 sql-mapping.xml 文件。


清单 13. 为 Books 增加多对多支持
                
<mapping>
 <class name="ibm.xml.castor.Book" identity="isbn">
 <map-to table="dw_books"/>
 <field name="isbn" type="string">
 <sql name="isbn" type="varchar" />
 </field>
 <field name="title" type="string">
 <sql name="title" type="varchar" />
 </field>
 <field name="authors" type="ibm.xml.castor.Author"
 collection="arraylist">
 <sql name="author_id"
 many-table="dw_books_authors"
 many-key="book_isbn" />
 </field>
 </class>

 <class name="ibm.xml.castor.Author" identity="id">
 <map-to table="dw_authors" />
 <field name="id" type="int">
 <sql name="id" type="integer" />
 </field>
 <field name="firstName" type="string">
 <sql name="first_name" type="varchar" />
 </field>
 <field name="lastName" type="string">
 <sql name="last_name" type="varchar" />
 </field>
 </class>
</mapping>

测试映射

清单 14 显示了修改后的 SQLTester,它创建了一个新的 Author 和相关的 Book


清单 14. 建立新的作者和图书
                
import java.util.Iterator;

import ibm.xml.castor.Author;
import ibm.xml.castor.Book;

import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.JDOManager;

public class SQLTester {
 public static void main(String args[]) {
 try {
 JDOManager.loadConfiguration("jdo-conf.xml");
 JDOManager jdoManager = JDOManager.createInstance("bmclaugh");

 Database database = jdoManager.getDatabase();
 database.begin();
 Author author = new Author(1001, "Joseph", "Conrad");
 Book book = new Book("1892295490", "Heart of Darkness", author);
 database.create(author);
 database.create(book);
 database.commit();

 database.begin();
 Book lookup = (Book)database.load(Book.class, "1892295490");
 System.out.println("Located book is named " + lookup.getTitle());
 System.out.println("Authors:");
 for (Iterator i = lookup.getAuthors().iterator(); i.hasNext(); ) {
 Author bookAuthor = (Author)i.next();
 System.out.println(" " + bookAuthor.getFirstName() + " " +
 bookAuthor.getLastName());
 }
 database.commit();
 database.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}

看起来再简单不过了。连接到数据库,创建新的作者和图书,然后保存到数据库(最后以 commit() 结束):

 Database database = jdoManager.getDatabase();
 database.begin();
 Author author = new Author(1001, "Joseph", "Conrad");
 Book book = new Book("1892295490", "Heart of Darkness", author);
 database.create(author);
 database.create(book);
 database.commit();

剩下的就是检查数据库看看数据是否成功存入了。

为了更好地模拟实际的数据持久,提交了数据然后启动了一个新事务(提交图书和作者数据之后的第二个 database.begin())。这样可以确保 Castor 数据的本地版本没有被使用,程序的第二部分实际上是查询数据库。

检验数据

看看表中的数据是否和预期的一样。表 dw_books 中应该包含 表 3 所示的数据。


表 3. 运行 SQLTester 后 dw_books 中的数据
isbn名称
1892295490Heart of Darkness

表 4 显示了插入之后的 dw_authors 表。


表 4. 运行 SQLTester 之后 dw_authors 表中的数据
idfirst_namelast_name
1001JosephConrad

表 5 显示了图书和作者之间的连接,从很多方面来说这是迄今您见过的最重要的数据。如果作者和图书不能联系起来,就没有多少用处了。


表 5. 运行 SQLTester 后 dw_books_authors 多对多表中的数据
book_isbnauthor_id
18922954901001

删除数据

清单 15 这个短小的实用程序删除 SQLTester 插入的数据。利用 SQL 数据绑定可以添加图书和作者(绝对应该这样做),与此类似,也可通过更新类来删除这些记录。保持类的独立便于在插入和删除之间检查数据库,这是学习和调试可能出现的任何问题的重要组成部分。


清单 15. 删除 SQLTester 创建的数据
                
import ibm.xml.castor.Author;
import ibm.xml.castor.Book;

import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.JDOManager;

public class SQLClean {
 public static void main(String args[]) {
 try {
 JDOManager.loadConfiguration("jdo-conf.xml");
 JDOManager jdoManager = JDOManager.createInstance("bmclaugh");
 Database database = jdoManager.getDatabase();
 database.begin();
 try {
 Book book = (Book)database.load(Book.class, "1892295490");
 database.remove(book);
 } catch (org.exolab.castor.jdo.ObjectNotFoundException ignored) { }
 try {
 Author author = (Author)database.load(Author.class, 1001);
 database.remove(author);
 } catch (org.exolab.castor.jdo.ObjectNotFoundException ignored) { }
 database.commit();
 database.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}

其中嵌入了应对没有可供删除的数据这种情况的异常处理语句,使得程序更健壮一点。





回页首


Castor 自身不能持久关系

观察 SQLTester清单 14),值得注意的是 Author Book 的实例都持久到了数据库。实际上如果访问其中一个就只能得到图书或者作者,而看不到 dw_books_authors 中的任何数据。

对于 Java 程序员来说,这似乎有点奇怪。如果一个 Book 实例有 Author,这两者难道没有联系在一起吗?没有作者的图书也能持久吗?不错,Castor 允许这种情况,而且这是它的默认行为。但是,如果希望 Castor 保持这种联系,可使用下列命令:

database.setAutoStore(true);

该命令必须在创建任何事务之前 发出,因此 清单 16清单 14 的基础上略加修改来完成这项操作。


清单 16. 自动存储关系的测试程序
                
import java.util.Iterator;

import ibm.xml.castor.Author;
import ibm.xml.castor.Book;

import org.exolab.castor.jdo.Database;
import org.exolab.castor.jdo.JDOManager;

public class SQLTester {
 public static void main(String args[]) {
 try {
 JDOManager.loadConfiguration("jdo-conf.xml");
 JDOManager jdoManager = JDOManager.createInstance("bmclaugh");

 Database database = jdoManager.getDatabase();
 database.setAutoStore(true);
 database.begin();
 Author author = new Author(1001, "Joseph", "Conrad");
 Book book = new Book("1892295490", "Heart of Darkness", author);
 // We can persist just the book, and Castor will handle the author, as well
 // database.create(author);
 database.create(book);
 database.commit();

 database.begin();
 Book lookup = (Book)database.load(Book.class, "1892295490");
 System.out.println("Located book is named " + lookup.getTitle());
 System.out.println("Authors:");
 for (Iterator i = lookup.getAuthors().iterator(); i.hasNext(); ) {
 Author bookAuthor = (Author)i.next();
 System.out.println(" " + bookAuthor.getFirstName() + " " +
 bookAuthor.getLastName());
 }
 database.commit();
 database.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}

试一下(如果数据库中还有数据首先运行 SQLClean),就会得到和原来相同的结果 — 除了新增的 database.create() 调用之外。虽然这只是一个小小的改进,但设想一下添加数百(或数千)本书及其作者的情形。节省不必要的工作至关重要。





回页首


结束语

虽然 XML 数据绑定已经非常稳定,在很大程度上不是增加而是修改,JDO — 包括 Sun 和 Castor 版本 — 仍然是一种通用的 API。但是,这并不意味要避免使用它,因为 SQL 数据绑定和 JDO(无论哪个厂商)都可以简化您的编程工作。以本文作为起点而不是权威性的指南来学习这一技术。

尝试一下 SQL 数据绑定看看是否有助于您的应用程序开发(这基本上是必然的)。然后比较一下 Sun 和 Castor 的 JDO 看看喜欢哪一种。最后选择一种使用。即便 Castor 的版本不是标准的,但是更加灵活,而且随着更深地参与 Sun,将来有一天官方标准可能纳入 Castor 的很多特性。

最重要的是,编程更加轻松。SQL 数据绑定可以帮助您提高效率,降低开发周期,最终提高您的工作质量以及所开发应用程序的质量。在同一个应用程序中使用 SQL 和 XML 数据绑定,两者采用了同样的映射范型。您将从中受益,也许最终会完全放弃 JDBC,没有人认为这样不好。






回页首


下载

描述名字大小下载方法
Java 源代码javaSourceCode.zip3KBHTTP
Castor 配置文件castorConfigurationFiles.zip1KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术
  • MySQL JDBC 驱动程序:从 MySQL.com 下载和了解这种原生 Java JDBC 支持。

  • Java and XML, Third Edition(Brett McLaughlin 和 Justin Edelson,O'Reilly Media, Inc.):全面掌握 XML,包括关于数据绑定和映射的扩展信息。

  • Java and XML Data Binding(Brett McLaughlin,O'Reilly Media, Inc.):在这部早期的著作中可以找到 Castor 的一些详细信息,以及数据绑定涉及的概念。

  • Castor Professional Services:您正在寻求关于 Castor 的付费支持或帮助吗?请了解一下 Castor 的专业服务。

  • IBM 试用软件:用这些试用软件开发您的下一个项目,可直接从 developerWorks 下载。


讨论


关于作者

Brett McLaughlin 的照片

Brett McLaughlin 的著作上过畅销榜并获得过非小说类图书奖。他著述丰富,包括计算机编程、家庭暴力、分析和设计,总印数超过 100,000。他编写、编辑和出版技术书籍快十年了,除了使用字符处理器外,他还喜欢弹奏吉他、和两个儿子在屋子里追逐嬉闹、和妻子观看重新开播的 Arrested Development。他的新著 Head First Object Oriented Analysis and Design 荣获 2007 Jolt Technical Book 大奖。经典著作 Java and XML 仍然是关于在 Java 语言中使用 XML 技术的权威书籍。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

将您的建议发给我们或者通过参加讨论与其他人分享您的想法.







回页首


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