|  | 级别: 中级 Robert Brunner (rb@ncsa.uiuc.edu), NCSA 研究科学家、天文学助理教授, University of Illinois, Urbana-Champaign
2007 年 10 月 22 日 了解如何构建一个自包含 (self-contained)、可部署的嵌入式 Apache Derby 数据库应用程序。本文是本系列的最后一篇文章,您将把数据库表映射到 Java™ 类中,编写 Data Access Object (DAO) 并将它们与业务逻辑类一同集成到完成的数据库应用程序中。您还将了解如何把应用程序与必要的 Derby 数据库文件打包在一起,创建一个包含数据库应用程序所有必要内容的压缩文件。
开发嵌入式 Apache Derby 应用程序
回想在 本系列的文章 中,您一直在使用 Apache Derby 数据库。Derby 数据库支持两种交互模式:网络 模式和嵌入 模式。较为熟悉的交互模式可能是网络模式,在这种模式下,数据库服务器运行在一台计算机上,并且数据库客户机通过网络连接连接到中央服务器上。这种设置描述了大多数商业数据库的安装设置,通常都是遵循事务处理模型。
 |
关于 Apache Derby 和 Java 的更多信息:
|
|
另一个模型是嵌入模式,在这种模式下,应用程序不但包含实现必要业务逻辑的软件,而且包含数据库引擎软件。最终,应用程序可以完全自包含,有助于简化安装和设置过程。为了强调这一点,因此考虑在本系列的所有文章中,始终只以嵌入模式使用 Apache Derby 数据库。
现在就要开始开发您自己的嵌入式 Derby 数据库应用程序。开发嵌入式数据库应用程序的过程一般可以分为三个步骤:
- 将数据库逻辑映射到 Java 类中。
- 开发管理数据库专有功能的 Java 代码。
- 实现 Java 应用程序中的业务逻辑。
这一节的其余部分先详细讨论这三个步骤,然后再继续讨论如何部署嵌入式 Apache Derby 应用程序。
对象-关系映射
本 系列 中的前几篇文章已经通过使用虚构的 Bigdog 的 Surf Shop 业务介绍了数据库概念。这个简单的数据库将管理冲浪器材店的存货,主要关注包含冲浪器材店产品的数据库表。在本文中,您将开发一个显示冲浪器材店存货中具体项目详细清单的应用程序。
 |
对象-关系映射软件
在本文中,您将显式构造 Java 代码,从而将关系数据库数据映射到 Java 类中。假定这个问题会给大型软件项目增加一定的复杂度,自动执行此过程可以减少发生软件错误的可能性。若干个商业和开源软件产品可以与 Apache Derby 数据库结合使用以自动执行此过程。您可以在本文的 参考资料 部分中找到这些产品的完整清单链接。
|
|
需要处理的第一步是实现映射到产品的 Java 类,该产品是 bigdog.products 表中的一行。在执行此操作时,您会遇到一个问题:表模式是在 SQL 中定义的,然而业务应用程序是用 Java 语言定义的。在 SQL 关系数据模型与 Java 对象模型之间映射有时会带来挑战,尤其是对于涉及通过主键-外键连接许多表的复杂数据库模式(或 Java 对象模型)。在形式上,此过程被称为对象-关系映射(Object-Relational Mapping,ORM)。
但是,对于这个演示应用程序,您将面对的惟一复杂度是如何把 bigdog.products 表的模式中使用的 SQL 数据库类型映射到适当的 Java 数据类型中。在本例中,过程相对简单,如清单 1 所示,在该清单中将定义 Product 类。
清单 1. 将产品表映射到 Java Product 类
public class Product {
private int itemNumber = 0 ;
private BigDecimal price = null ;
private Date stockDate = null ;
private String description = null ;
public Product(int itemNumber, BigDecimal price, Date stockDate, String description){
this.itemNumber = itemNumber ;
this.price = price ;
this.stockDate = stockDate ;
this.description = description ;
}
public void printProduct() {
String line = "------------------------------------" ;
System.out.println("\nBigdog's Surf Shop Product Information") ;
System.out.println(line + line);
System.out.printf("Item Number : %-11s\n", this.itemNumber) ;
System.out.printf("Item Price : $%-8.2f\n", this.price) ;
System.out.printf("Item StockDate : %-10s\n", this.stockDate) ;
System.out.printf("Item Description : %-40s\n", this.description) ;
System.out.println(line + line + "\n");
}
... |
由于本系列的 第十篇文章 中从 bigdog.products 表中提取数据时介绍了关于执行查询的课程,因此您应对这个清单中提供的映射有些熟悉。该类定义了将 bigdog.products 表中的列与适当的 Java 数据类型相匹配的属性。在实践中,您可以为每个属性添加完整的 getter/setter 方法,虽然在这段演示代码中并没有这样做,但是大多数流行开发环境通常都会自动完成此操作。
Product 类中的两个主要方法是根据需要初始化对象的构造函数,以及为特定产品生成格式良好的输出的 printProduct 方法。此方法将被演示应用程序调用,但是放置在此类中以利用紧密耦合的优点 —— 毕竟,谁能比定义类更了解输出对象的方法?通常,您要为必须向业务应用程序公开的数据库中的每张表定义 Java 类。因此,虽然这样做对于这个简单的演示并不必要,但是您也可以构造一个 Java 类映射到 bigdog.vendors 表。
封装数据库逻辑
在数据库表被成功地映射到 Java 类中后,下一步是把数据库表与相应的 Java 类连接起来。一种可能的做法是将这段连接代码直接放到相应的 Java 类中 —— 例如,清单 1 中提供的 Product 类。但是,在大多数情况下,这样做不好,因为这将在数据库与数据库应用程序之间引入紧密连接。就像您在本 系列 中被反复警告的那样,通常应当尽量避免使用紧密耦合。
更好的方法是实现一个接口层,在该层中可以封装特定于数据库的 Java 代码。在这种方法中,如果数据库详细保存了更改,例如位置、版本甚至基本模式修改,则应用程序将被隔离;您需要做的惟一更改仅限于更改 DAO。此中间层中的 Java 类被称为 DAO,并且连接到数据库表的每个 Java 类通常都有一个 DAO 类。例如,在清单 2 中,ProductDAO 类提供了先前定义的 Product 类的 DAO。
清单 2. 把产品表中的行链接到 Java Product 类
SELECT itemNumber, price, stockDate, description " +
"FROM bigdog.products WHERE itemNumber = ?" ;
...
public Product getProduct(int targetItemNumber){
Product product = null ;
try{
pstmt.clearParameters() ;
pstmt.setInt(1, targetItemNumber) ;
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
int itemNumber = rs.getInt("itemNumber") ;
BigDecimal price = rs.getBigDecimal("price") ;
Date stockDate = rs.getDate("stockDate") ;
String description = rs.getString("description") ;
product = new Product(itemNumber, price, stockDate, description) ;
}
rs.close() ;
} catch(SQLException se) {
printSQLException(se) ;
}
return product;
}
... |
ProductDAO 类的主要用途是创建一个由 bigdog.products 表的特定行中的数据组成的新 Product 对象。该行是由传入 getProduct 方法中的 targetItemNumber 值识别的。虽然它没有显示在 清单 2 中,但是此类还将通过发出 SELECT MAX(itemNumber) FROM bigdog.products 查询并分析查询结果,确定最大项目数(演示应用程序需要)。
如果查看 清单 2,您可以轻松地看到底层数据库模式与 DAO 类之间的紧密耦合。在 getProduct 方法中,将通过执行 PreparedStatement 提取数据。通过添加附加层,最大限度地降低任何数据库更改所产生的影响 —— 对于微小更改,此演示应用程序中惟一需要更新的类是 ProductDAO。
您还可以把数据库管理代码放到一个单独的类中以封装这类功能,如清单 3 所示。
清单 3. 数据库管理代码
public class DBManager {
private static Connection con = null ;
private static final String driver = "org.apache.derby.jdbc.EmbeddedDriver" ;
private static final String url = "jdbc:derby:" ;
private static final String dbName = "SurfShop" ;
...
private ProductDAO pdao = null ;
public DBManager(){
if(!dbExists()){
try {
Class.forName(driver) ;
con = DriverManager.getConnection(url + dbName + ";create=true");
processStatement(createProductsSQL) ;
con.setAutoCommit(false) ;
batchInsertData(insertProductsSQL) ;
con.commit() ;
}catch(BatchUpdateException bue) {
...
}
}
pdao = new ProductDAO(con) ;
}
public void close() {
try {
con = DriverManager.getConnection(url + ";shutdown=true") ;
}catch(SQLException se) {
; // Do Nothing. System has shut down.
}
con = null ;
}
public ProductDAO getProductDAO() {
return pdao ;
}
private Boolean dbExists() {
Boolean exists = false ;
try{
Class.forName(driver) ;
con = DriverManager.getConnection(url + dbName);
exists = true ;
} catch(Exception e){
; // Do nothing, as DB does not (yet) exist
}
return(exists) ;
}
... |
清单 3 中提供的 DBManager 类是此应用程序中最大的类。有一部分原因是因为演示应用程序比较简单,但是它也反映了从 Java 应用程序中与数据库进行成功交互的复杂度。此类负责管理数据库连接,包括建立初始连接和创建演示应用程序要访问的 ProductDAO 实例。
由于这是嵌入式应用程序,因此这段代码还负责创建和初始化嵌入式数据库本身。在这个类中,您将通过使用 dbExists 方法完成此操作,该方法将尝试建立数据库连接。如果数据库不存在,将抛出一个异常,并且 DBManager 构造函数自己将创建数据库。然后它将创建 bigdog.products 表,并且最后通过一次批插入操作插入 10 个新行。遵循这个逻辑,如果该应用程序被执行第二次,则 dbExists 方法将成功连接(假定不知何故并未删除本地存储的数据库),并跳过数据库创建过程。为了简化各种数据库连接操作,JDBC URL 被编码为 jdbc:derby,并且根据所需的操作附加不同的后缀。例如,url + dbName + ";create=true" 用于创建数据库,而 url + dbName 用于建立数据库连接。
关于 DBManager 类的最后一个要点是提供 close 方法。此方法将通过尝试用附加在基本 JDBC URL 中的数据库属性 shutdown=true 建立数据库连接,显式关闭 Apache Derby 嵌入式数据库。通过这种方式关闭数据库,可以保证所有事务都将被正常关闭。这将使得数据库可以更加快速地重新启动,因为数据库将一直处于前后一致状态。
开发嵌入式应用程序
过程的最后一个正式步骤是开发 Java 应用程序。为了保持简单,此演示应用程序:
- 在命令行中接受来自用户的数字输入
- 检索拥有输入项目号的产品信息
- 显示用户要查看的产品
给定先前定义的类,演示应用程序代码相对简短并且易于实现,如清单 4 所示。
清单 4. 嵌入式 Apache Derby 应用程序代码
import java.io.BufferedReader ;
import java.io.InputStreamReader ;
public class DemoApp {
private DBManager dbm = null ;
private ProductDAO pdao = null ;
private int maxItemNumber = 0 ;
public DemoApp() {
this.dbm = new DBManager() ;
this.pdao = dbm.getProductDAO() ;
this.maxItemNumber = pdao.getMaxItemNumber() ;
}
public void showProduct() {
int itemNumber = 0 ;
Product p = null ;
BufferedReader cin = new BufferedReader(new InputStreamReader(System.in)) ;
while(true) {
try {
System.out.print("Enter object item number (0 to Exit): ") ;
itemNumber = Integer.parseInt(cin.readLine()) ;
}catch(Exception ex){
; // Do nothing. In this simple demo you ignore any input errors.
}
if(itemNumber == 0)
break ;
else if ((itemNumber < 0) || (itemNumber > this.maxItemNumber))
System.out.println ("Invalid product item number, please try again.") ;
else{
p = pdao.getProduct(itemNumber) ;
p.printProduct() ;
}
}
dbm.close() ;
}
public static void main(String[] args) {
DemoApp da = new DemoApp() ;
da.showProduct() ;
}
} |
清单 4 中所示的应用逻辑包含在这个类的两个方法中:构造函数和 showProduct 方法。构造函数将按照前面的描述创建一个 DBManager 对象以建立数据库连接,然后获得 ProductDAO 类的实例。此实例将在 showProduct 方法中使用,用于从数据库中检索必要的数据。构造函数还将从数据库检索最大项目数,这将简化错误管理。
showProduct 方法将从控制台的标准输入流中读取一个整数。如果该整数值是 bigdog.products 表的允许值,则将通过使用 bigdog.products 表中的相应数据创建 Product 对象,并且使用 printProduct 方法显示该值。此过程将一直持续,直到用户输入零以终止循环为止。
部署嵌入式 Apache Derby 应用程序
在成功地实现并测试嵌入式 Apache Derby 数据库应用程序之后,下一步是把所有必要的文件打包在一起,以便您可以根据需要部署应用程序。这一节的其余部分将指导您完成先前构造的演示应用程序的打包过程。第一步(如果尚未这样做)是将可从 下载 部分获得的样例代码文件解压缩,如清单 5 所示。
清单 5. 准备构建嵌入式 Apache Derby 应用程序
rb$ mkdir derbyWork
rb$ cd derbyWork/
rb$ unzip ../derby14.zip
Archive: ../derby14.zip
inflating: DBManager.java
inflating: DemoApp.java
inflating: Product.java
inflating: ProductDAO.java
inflating: manifest.txt
rb$ more manifest.txt
Main-Class: DemoApp
Class-Path: lib/derby.jar
rb$ |
如您所见,第一步是创建一个新的工作目录,然后把样例代码文件解压缩到这个新目录中。压缩样例代码文件包括五个文件:演示应用程序的四个 Java 源代码文件和 manifest.txt 文件,其中的内容如 清单 5 所示,由包含名称-值对的两行组成。您将使用这个 manifest 文件为演示应用程序构建可执行的 JAR 文件。第一个名称-值对将指定主要应用程序驱动程序的类的名称,本例中为 DemoApp。第二个名称-值对将指定查找 Apache Derby 数据库 Java 类的位置,这些嵌入模式下的类全都包含在 derby.jar 文件中。
下一步是把必要的文件一起打包到 .jar 文件中,如清单 6 所示。
清单 6. 构建嵌入式 Apache Derby 应用程序
rb$ mkdir lib
rb$ cp $DERBY_INSTALL/lib/derby.jar lib/
rb$ javac *.java
rb$ jar cvmf manifest.txt demoapp.jar *.class
added manifest
adding: DBManager.class(in = 5087) (out= 2792)(deflated 45%)
adding: DemoApp.class(in = 1460) (out= 881)(deflated 39%)
adding: Product.class(in = 1684) (out= 919)(deflated 45%)
adding: ProductDAO.class(in = 2625) (out= 1369)(deflated 47%)
rb$ ls
DBManager.class Product.class demoapp.jar
DBManager.java Product.java lib
DemoApp.class ProductDAO.class manifest.txt
DemoApp.java ProductDAO.java
rb$ rm -rf *.java *.class manifest.txt
rb$ ls
demoapp.jar lib
rb$ zip demoapp.zip demoapp.jar lib/derby.jar
adding: demoapp.jar (deflated 6%)
adding: lib/derby.jar (deflated 8%)
rb$ rm -rf demoapp.jar lib
rb$ ls
demoapp.zip |
 |
交付 Apache Derby 软件
虽然 Apache Derby 数据库是开源软件产品,但是有许可要求。幸运的是,嵌入式应用程序只有一个要求:您必须将 Apache Derby 2.0 版许可副本(有关链接,请参阅 参考资料)连同包括任何 Apache Derby 数据库软件的任何软件产品一起交付。
|
|
该过程的第一步是根据 manifest 文件中所示的 CLASS-PATH: lib/derby.jar 名称-值对的要求,创建 lib 目录,然后把 derby.jar 文件复制到这个目录中。如果要将该值改为其他值,请相应地更改 derby.jar 文件的位置,否则部署后的演示应用程序将失败(因为它将无法找到必需的 Apache Derby 类文件)。
接下来,您需要编译包含的演示应用程序源代码,然后创建 JAR 文件。您将利用 cvmf 标记调用 jar 可执行文件以创建 JAR 文件,cvmf 将指定创建 (create) 归档文件,详细说明 (verbose) 所采取的所有步骤,使用指定的 manifest 文件,并将得到的 JAR 文件 (file) 命名为有象征性的名称。当您以这种方式调用该可执行文件时,您必须先指定 manifest 文件,后接需要调用 JAR 文件的名称。最后,必须显式列出新 JAR 文件中应当包含的所有文件或目录。
在创建了包含应用程序代码的 JAR 文件后,您可以安全地删除那些文件(当然,应当在其他位置保存一个备份副本),然后创建包含应用程序 JAR 文件和所需支持类的新压缩文件(在本例中,就是 derby.jar 文件)。最后,为了演示新嵌入式 Apache Derby 数据库应用程序的自包含本性,您可以删除其他所有文件。关于打包嵌入式应用程序的最后一点是:在本例中不交付任何 Apache Derby 软件。因此,不需要包括任何 Apache Derby 许可信息。如果您曾经交付 Apache Derby 数据库软件 —— 例如,把本例发送给其他计算机上的其他人 —— 则需要遵循 交付 Apache Derby 软件 侧栏中说明的 Apache Derby 数据库许可要求。
现在您已经创建了一个包含整个嵌入式 Apache Derby 应用程序的新压缩文件,剩下的惟一工作就是运行演示应用程序,如清单 7 所示。
清单 7. 执行嵌入式 Apache Derby 应用程序
rb$ unzip demoapp.zip
Archive: demoapp.zip
inflating: demoapp.jar
creating: lib/
rb$ rm demoapp.zip
rb$ CLASSPATH=
rb$ export CLASSPATH
rb$ echo $CLASSPATH
rb$ java -jar demoapp.jar
Enter object item number (0 to Exit): -1
Invalid product item number, please try again.
Enter object item number (0 to Exit): 11
Invalid product item number, please try again.
Enter object item number (0 to Exit): 1
Bigdog's Surf Shop Product Information
------------------------------------------------------------------------
Item Number : 1
Item Price : $19.94
Item StockDate : 2006-03-31
Item Description : Hooded sweatshirt
------------------------------------------------------------------------
Enter object item number (0 to Exit): 10
Bigdog's Surf Shop Product Information
------------------------------------------------------------------------
Item Number : 10
Item Price : $34.95
Item StockDate : 2006-01-24
Item Description : Open-toed sandal
------------------------------------------------------------------------
Enter object item number (0 to Exit): 0
rb$ ls
SurfShop demoapp.jar derby.log lib |
要运行演示应用程序,需要展开压缩文件,然后执行 demoapp.jar 文件。作为额外的补充,此示例还将删除压缩应用程序文件并清除 shell 的 CLASSPATH 环境变量的值。因此,您知道这个演示应用程序将在未安装完整的 Apache Derby 数据库软件的计算机上运行。
运行时,这个演示应用程序将创建 Surf Shop 数据库,用 10 行填充 bigdog.products 表,然后继续检索产品信息,直到用户输入零以终止程序为止。虽然简单,但是这个演示应用程序演示了嵌入式 Apache Derby 应用程序的主要要求。您可以轻松地获得此示例,并将其扩展为允许用户更新、插入或删除 bigdog.products 表中的行。或者,您可以使用它作为您自己的嵌入式 Apache Derby 数据库应用程序的基础。

 |

|
结束语
在本 系列 的最后一篇文章中,结合了前面几篇文章中提供许多课程以开发和部署一个完整的嵌入式 Apache Derby 数据库应用程序。了解了如何将 Derby 数据库表映射到 Java 类中,如何构造 DAO,以及如何成功地进行整合。您还了解了如何将 Derby 应用程序与 Apache Derby 数据库 Java 代码集成到自包含的可部署 Java 应用程序。掌握本系列文章中介绍的知识,您就能够构建您自己的嵌入式 Apache Derby 数据库应用程序 —— 那就去实践吧!
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 本文的 Java 代码 | derby14.zip | 4KB | HTTP |
|---|
参考资料 学习
获得产品和技术
讨论
关于作者  | 
|  | Robert J. Brunner 是 National Center for Supercomputing Applications 的研究科学家,也是位于 Urbana-Champaign 的伊利诺斯大学的天文学助理教授。他出版过多部著作,发表过主题广泛的文章和教程。 |
对本文的评价
|  |
Cloudscape、IBM 和 IBM 徽标是 IBM 在美国和/或其他国家的注册商标。 Java 和所有基于 Java 的商标是 Sun Microsystems, Inc. 在美国和/或其他国家的商标。 其他公司、产品或服务的名称可能是其他公司的商标或服务标志。 IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。 |