级别: 高级 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);
}
} |
清单 2 是 Author 类的代码。
清单 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_ 前缀,以便于其他表区分开来,特别是 authors 和 books 都是很常见的表名。虽然可能有点罗嗦,但如果按照本文实验(希望如此)的话这样可以避免与系统中其他数据库的命名冲突。
接下来是 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 列 |
|---|
| firstName | first_name | | lastName | last_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 列 |
|---|
| id | id | | firstName | first_name | | lastName | last_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.commit();
database.close(); |
添加关系数据
可以看到,处理单一的类到表的映射并不难。但是一旦需要处理更加实际的情况,情况就复杂了,比如 Book 与 Author 实例关联的 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)以及如何映射对象的属性(isbn 和 title)。
定义多对多关系
现在情况复杂了:告诉 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 属性,但其类型是类而不再是 string 或 int。使用 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 | 名称 |
|---|
| 1892295490 | Heart of Darkness |
表 4 显示了插入之后的 dw_authors 表。
表 4. 运行 SQLTester 之后 dw_authors 表中的数据
| id | first_name | last_name |
|---|
| 1001 | Joseph | Conrad |
表 5 显示了图书和作者之间的连接,从很多方面来说这是迄今您见过的最重要的数据。如果作者和图书不能联系起来,就没有多少用处了。
表 5. 运行 SQLTester 后 dw_books_authors 多对多表中的数据
| book_isbn | author_id |
|---|
| 1892295490 | 1001 |
删除数据
清单 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.zip | 3KB | HTTP |
|---|
| Castor 配置文件 | castorConfigurationFiles.zip | 1KB | HTTP |
|---|
参考资料 学习
获得产品和技术
讨论
关于作者  | 
|  | Brett McLaughlin 的著作上过畅销榜并获得过非小说类图书奖。他著述丰富,包括计算机编程、家庭暴力、分析和设计,总印数超过 100,000。他编写、编辑和出版技术书籍快十年了,除了使用字符处理器外,他还喜欢弹奏吉他、和两个儿子在屋子里追逐嬉闹、和妻子观看重新开播的 Arrested Development。他的新著 Head First Object Oriented Analysis and Design 荣获 2007 Jolt Technical Book 大奖。经典著作 Java and XML 仍然是关于在 Java 语言中使用 XML 技术的权威书籍。 |
对本文的评价
|