(转帖)通过 JPOX 和 DB2 Universal Database 了解 JDO 2.0

  把roller2.0导入eclipse2.1中编译提示缺少jdo的类,于是在网上搜索到这篇文章,贴在这里备忘。

       中国 [ 选择]      使用条款

  

 

 

    dW 全部内容 -----------------   DB2   Lotus   Tivoli   WebSphere -----------------   Java 技术   Linux   Open source   Security   SOA & Web services   Web architecture   Wireless   XML ----------------- IBM 全部内容            

 

 

      首页       产品       服务与解决方案       支持与下载       个性化服务       

 

 

 

developerWorks

中国

本文内容包括:

 JDO 2.0 业务案例

 先决条件

 首先决定要持久存储的数据

 编译 Employee.java

 为对象-关系映射创建 XML 元数据

 创建一个 log4j.properties 文件

 增强 Employee.class

 持久存储 Employee 对象的示例应用程序

 编译和运行 MyApplication.java

 出错时分析 JPOX 日志

 在 J2EE Application Server 中使用 JPOX 

 结束语

 参考资料 

 作者简介

 对本文的评价

 

相关链接

 DB2 technical library

 Java technology technical library

 

 

 

developerWorks 中国  >  DB2 | Java technology  >

通过 JPOX 和 DB2 Universal Database 了解 JDO 2.0 

 

  文档选项

   将此页作为电子邮件发送

 

 

对此页的评价

  帮助我们改进这些内容

 

 

 

级别: 初级

Robert Peterson, WebSphere Enablement, Austin, TX, IBM

Kulvir Bhogal, Software Services for WebSphere, Dallas, TX, IBM

2005 年 6 月 23 日

Java Community Process (JCP) 引入的 Java™ Data Objects (JDO) 规范是用于将纯 Java 对象持久存储到诸如 RDBMS 之类的数据存储的一种框架。Java Persistent Objects (JPOX) 是遵从 JDO 1.0 (JSR 012) 和 JDO 2.0 (JSR 243) 的一种实现。本文展示如何使用 JPOX 实现来抽象用于将数据持久存储到 DB2® Universal Database™ (DB2 UDB) 数据库和从中检索数据的 SQL 的单调乏味的编写工作。我们展示了 JPOX 1.1,它被 Java Community Process 选作 Java Data Ojects 2.0 的参考实现。JPOX 是在 Apache 2.0 开放源代码许可下发布的,可以免费使用。

JDO 2.0 业务案例

JDO 为基于开放标准的快速开发提供了便利,几乎可用于任何 Java 环境。如果项目预算有限,并且需要在相对较短的时间周期内以尽量少的 bug 发布,那么 JDO 可以作为处理持久性的上选。下面让我们更详细地分解 JDO 的一些关键特征:

JDO 2.0 是一种标准。 JDO 相对于其他 Java 对象-关系映射框架的一个优势是,作为 Java Specification Request (JSR) 243,它是 Java Community Process (http://www.jcp.org) 的一部分。它是一种标准 API,任何人都可以为之创建一个实现(在本文中,我们使用 JPOX 1.1 实现)。如果您想换用不同的 JDO 实现,不管是商业的还是开放源代码的,无需更改任何用于访问 JDO API 的代码就可以做到这一点(假设没有使用不属于 JDO 2.0 标准的特定于 JPOX 的扩展)。

可以在任何 Java 1.3+ 环境下使用。 JDO 被设计成可以在任何 Java(1.3 或更高版本)环境下使用,包括在 J2EE Web 和 EJB 容器中。JDO 适用于大多数支持 JDBC 的主流数据库,并且可以映射到已有的模式。请参阅 http://www.jpox.org/docs/1_1/rdbms.html 查看 JPOX 所适用的数据库列表。DB2 就属于这些数据库中的一种。

学习 JDO 只需很少的培训。 从本文可以看出,只需掌握基本的 Java 技巧,就可以创建和持久存储 JDO 对象。

JDO 可以缩短发布周期,并提高持久性代码的软件质量。 复杂查询字符串、范式优化和 SQL 的过程性质都通过 JDO 得以抽象,从而使开发小组可以将精力集中在他们的应用的持久性需求这个核心上。由于更多的应用程序代码变为通过 JDO 库来透明地处理,因此可以减少 bug,同时也可加快发布的时间。

JDO 是成熟的。 您可能会想:“它真能在我的后端环境中工作吗?” JDO 2.0 规范引入了生产系统所需的大多数高级持久性功能,包括非常丰富的查询开发(必要时还包括常规的 SQL)以及可以使用已有数据库模式的对象-关系映射技术。

JPOX 是在 Apache Public License 下发布的。 这是一种非常友好的许可,它为那些用 JPOX 框架作开发的人提供了很大的自由。事实上开源项目并非总是如此友好,例如,作为另一种流行 ORM 框架的 Hibernate 则使用 GNU Lesser General Public License (LGPL),这种许可具有更多的限制,尤其是当您想将自己的作品变成专利的时候更是多有不便(请参阅 http://www.gnu.org/copyleft/lesser.html)。

懂得权衡。 与其他持久性技术,例如 Container Managed Persistence (CMP) 和 JDBC 相比,当前的 JDO 提供程序在同类技术中显然没有得到最优先的支持。虽然 JDO 规范在不断进步,但是它仍未被广泛采纳,并且要落后其他持久性技术(例如实体)几个版本。

 

 回页首

 

先决条件

为了试验本文中描述的实现,需要用到下列软件:

Java SDK 1.3 或更高版本

IBM DB2 V8.x 或更高版本(从 developerWorks 下载页面 可以获得免费使用 90 天的试用版)。

以下文件也是必需的:

JPOX JAR 文件。 JPOX 的 JDO 2.0 规范实现封装在一个 JAR 文件中。请从 http://www.jpox.org/docs/download.html 下载 jpox-1.1.0-beta-3.jar(或更高版本)。另外还要从这个站点下载 JPOX-enhancer-1.1.0-beta-3.jar(或更高版本)。JPOX enhancer 是一种工具,它可以将编写 JDO 用于持久存储每个对象的方法这样单调的任务自动化(后面有更多解释)。

JDO 2.0 JAR。 这个 jar 文件包含 JDO 2.0 API 或 javax.jdo.* 接口。请从前面所说的 JPOX 下载站点下载 jdo-2.0.jar。它位于标题“Sun JDO 2.0”下的列表中接近底部的位置。

Apache Log4J JAR 文件。 JPOX 使用 Apache Log4J 对事件和错误作日志记录。请从 http://logging.apache.org/log4j 下载 logging-log4j-1.2.9.jar(或更高版本),它位于给出的 zip 文件的 /dist/lib 目录下)。

Apache BCEL JAR 文件。 JPOX enhancer 需要 Apache 字节码工程库(byte code engineering library,BCEL)。请从 http://jakarta.apache.org/bcel/ 下载 bcel-5.1.jar(或更高版本)。

为简单起见,在本文中对上述 JAR 文件作如下重命名。建议您也这样做,以便不作修改地复制粘贴本文中的各种命令:

               jpox.jar

               jpox-enchancer.jar

               jdo-2.0.jar

               log4j.jar

               bcel.jar

带 Type 4 驱动程序的 DB2 JAR。 需要 db2jcc.jar 和 db2jcc_license_cu.jar。默认情况下这两个 jar 文件位于以下目录中:

               Linux 或 UNIX®:             /opt/IBM/db2/V8.x/java

               Windows®:             c:\program files\IBM\SQLLIB\java

目录。 最后,创建以下目录结构:

            /jdo-example —— 将所有 7 个 jar 文件放在该目录中。

            /jdo-example/src/example —— 该目录将包括所有源文件(*.java)。

            /jdo-example/classes/example —— 该目录将包括所有二进制文件(*.class)。

            /jdo-example/admin —— 该目录将包括与配置和部署相关的文件。

 

 回页首

 

首先决定要持久存储的数据

对于本文,我们将持久存储一个模拟工资表系统中的雇员信息。用 JDO 设计持久性对象的高级方法有两种:由上到下(top-down) 和由下到上(bottom-up)。在由上到下的方法中,先设计应用程序所需的 Java 对象,然后由 JDO 生成数据库模式,并执行对象-模式映射。JPOX 1.1 Tutorial(见 参考资料)中包含了由上到下方法的一个例子。

本文使用由下到上的方法,这种方法获取已有的模式,并创建到相应的可持久 Java 对象的 JDO 对象-关系映射。我们要持久存储的数据是雇员的社会安全号、名字、姓氏以及薪水。以雇员的社会安全号为主键的数据库表如下:

图 1. 雇员数据库表

 

当决定了要持久存储的数据和拟采用的方法之后,下一步是创建一个 DB2 数据库和一个 EMPLOYEE 表。启动 DB2 命令行处理器。默认情况下,这个工具位置如下:

Linux 或 UNIX

               /opt/IBM/db2/V8.x/bin/db2

Windows

               c:\Program Files\IBM\SQLLIB\bin\db2.exe

在 Windows 中,为了设置命令行处理器的 shell 环境,首先必须运行 db2cmd.exe 命令。启动了命令行处理器之后,执行以下命令:

清单 1. 用于设置 EMPLOYEE 表的 DB2 命令

db2 => db2start

db2 => create database MYDB

db2 => connect to MYDB user db2admin using db2admin

db2 => create table EMPLOYEE(SOCIAL bigint not null, FIRST_NAME varchar(20), LAST_NAME varchar(20),

  SALARY real, primary key(SOCIAL))

db2 => commit

 

注意,上面清单中假设 db2admin 是数据库管理员的用户名和密码。

下一步是创建要持久存储的对象。当编写一个将来由 JDO 持久存储的类时,类中数据成员的类型很重要。所有 Java 基本类型(int、char 和 float 等)以及 java.util 库中的大多数对象(例如 java.util.Collection 和 java.util.Map)都受支持。如果您想持久存储的对象是不受支持的,那么可以定义自己的对象。关于 JPOX 直接支持的类型列表,请参阅 http://www.jpox.org/docs/1_1/types.html。

现在编写将被持久存储到 EMPLOYEE 表中的 Java 类。创建下面这个类,并将其放入 jdo-example/src 目录中:

清单 2. Employee.java

package example;

public class Employee {

private long social;

private String firstName;

private String lastName;

private float salary;

/**

* Constructor

*/

Employee( long social, String firstName, String lastName, float salary ) {

this.social = social;

this.firstName = firstName;

this.lastName = lastName;

this.salary = salary;

}

void setSocial(long social) {

this.social = social;

}

long getSocial() {

return social;

}

void setFirstName(String firstName) {

this.firstName = firstName;

}

String getFirstName() {

return firstName;

}

void setLastName(String lastName) {

this.lastName = lastName;

}

String getLastName() {

return lastName;

}

void setSalary(float salary) {

this.salary = salary;

}

float getSalary() {

return salary;

}

}

 

 

 回页首

 

编译 Employee.java

使用您喜欢的构建工具,或者在 jdo-example 目录下的 shell 中输入以下命令,编译要持久存储的对象:

               > javac -d classes src/example/Employee.java

 

 回页首

 

为对象-关系映射创建 XML 元数据

JDO 需要一个 XML 文件来定义要持久存储的字段,以及应该将这些字段映射到什么样的 JDBC 或 JDO 包装器结构中。该文件定义从 EMPLOYEE 数据库表到 Employee Java 对象的映射,如下所示:

图 2. 对象-关系映射

 

创建一个包含以下内容的名为 package.jdo 的 XML 文件,并将其放入到 jdo-example/admin 目录中:

清单 3. package.jdo

<?xml version="1.0"?>

<!DOCTYPE jdo PUBLIC  "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN" "http://java.sun.com/dtd/jdo_2_0.dtd">

<jdo>

    <package name="example">

        <class name="Employee" identity-type="application" table="EMPLOYEE">

            <field name="social" primary-key="true">

                <column name="SOCIAL" jdbc-type="BIGINT"/>

            </field>

            <field name="firstName">

                <column name="FIRST_NAME" length="20" jdbc-type="VARCHAR"/>

            </field>

            <field name="lastName">

                <column name="LAST_NAME" length="20" jdbc-type="VARCHAR"/>

            </field>

            <field name="salary">

                <column name="SALARY" jdbc-type="REAL"/>

            </field>

        </class>

    </package>

</jdo>

 

为了更好地理解映射过程,让我们来看一下 class 标记:

               <class name="Employee" identity-type="application" table="EMPLOYEE">

这包括表那一级上的映射信息。除了 Java class 名和数据库 table 名之外,indentity-type 还指定 JDO 是否控制 Employee 的 id(在数据库中是主键)。身份类型 datastore 指示 JDO 自动创建和管理被持久存储的对象的 id,身份类型 application 允许开发人员指定键(本文使用这种方法)。

现在考虑第一个字段:

               <field name="social" primary-key="true">

                                             <column name="SOCIAL" jdbc-type="BIGINT"/>

               </field>

field 标记的 name 属性定义 Employee Java 对象数据成员的名称。primary-key 属性指定数据库列及其在 Java 对象中相应的数据成员是 Employee 的惟一标识符。column 标记的 name 属性定义将被映射到 Employee 数据成员的列名。jdbc-type 指定应该用于映射数据库中列的数据类型的 JDBC 数据类型。

 

 回页首

 

创建一个 log4j.properties 文件

JPOX 使用 Apache log4J 来报告错误。这要求当您增强类(类的增强在下一节中介绍)以及创建一个 log4J 属性文件时,将 log4j.jar 包括在类路径中。创建一个包含以下内容的 log4j.properties 文件,并将其放入 jdo-example/admin 目录:

清单 4. log4j.properties

# Define the destination and format of our logging

log4j.appender.A1=org.apache.log4j.FileAppender

log4j.appender.A1.File=jpox.log

log4j.appender.A1.layout=org.apache.log4j.PatternLayout

log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} (%t) %-5p [%c] - %m%n

# JPOX Categories

log4j.category.JPOX.JDO=INFO, A1

log4j.category.JPOX.Cache=INFO, A1

log4j.category.JPOX.MetaData=INFO, A1

log4j.category.JPOX.General=INFO, A1

log4j.category.JPOX.Utility=INFO, A1

log4j.category.JPOX.Transaction=INFO, A1

log4j.category.JPOX.RDBMS=DEBUG, A1

log4j.category.JPOX.Enhancer=INFO, A1

log4j.category.JPOX.SchemaTool=INFO, A1

 

如果要定制默认的属性文件,或者只是想了解更多关于 JPOX 如何使用 Apache Log4J 的信息,请参考 http://www.jpox.org/docs/1_1/logging.html。

 

 回页首

 

增强 Employee.class

为了将 Employee 对象持久存储到数据库或者从数据库中检索 Employee 对象,需要添加一些附加的方法。Employee 类必须实现一个特殊的接口,即 javax.jdo.spi.PersistenceCapable,这个接口有八个静态字段和二十多个方法。虽然可以为要持久存储的每个类都手动地实现该接口,但在实践中这是使用 JPOX 库并通过字节码来自动完成的,这个字节码“增强”了要持久存储的对象的类文件。

使用 /jdo-example 目录中的以下命令来增强 Employee 类:

Linux 或 UNIX

               > java -cp classes:jdo-2.0.jar:jpox.jar:bcel.jar:jpox-enhancer.jar:log4j.jar

           -Dlog4j.configuration=file:admin/log4j.properties org.jpox.enhancer.JPOXEnhancer admin/package.jdo

Windows

               > java -cp classes;jdo-2.0.jar;jpox.jar;bcel.jar;jpox-enhancer.jar;log4j.jar

           -Dlog4j.configuration=file:admin\log4j.properties org.jpox.enhancer.JPOXEnhancer admin\package.jdo

命令的输出如下:

               JPOX Enhancer (version 1.1.0) : Enhancement of classes

               ENHANCED: example.Employee

还应注意的是,现在在 /jdo-example 中有一个 jpox.log 文件。如果在本文的实践当中遇到任何错误,都可以查看这个日志。

 

 回页首

 

持久存储 Employee 对象的示例应用程序

下面的代码是使用 Employee 类的应用程序的一个例子,它创建 Employee 类的一个实例,填充这个实例,然后将它持久存储到一个 IBM DB2 数据库中。在开发使用 JDO 的应用程序时,关键的部分是 javax.jdo.PersistenceManager。这个对象创建事务并持久存储 JDO 对象。要了解关于 PersistenceManager 的更多信息,请参阅

               http://jcp.org/aboutJava/communityprocess/pr/jsr243/index.html 中的 JDO 2.0 规范。

该应用程序创建一个 Employee 实例,将这个 Employee 实例持久存储到 DB2 中,并更新雇员的信息。最后,它从数据库中删除 Employee,从而执行了 Create、Update 和 Delete(即 CRUD)。为了更好地观察该应用程序的行为,我们插入一些 pause 语句,以打断应用程序的执行,使之直到用户在命令行按下‘Enter’键才继续执行。这样一来,在每次操作过后,就可以查看 DB2 中表的内容。

下面我们不会一下子给出完整的类,而是每次给出一个方法。首先来看这个类的数据成员和 main 方法。创建一个如下所示的 MyApplication.java 类,并将其放入 /jdo-example/src/example 目录。

清单 5. MyApplication.java

package example;

import java.io.IOException;

import java.util.Collection;

import java.util.Properties;

import javax.jdo.JDOHelper;

import javax.jdo.PersistenceManager;

import javax.jdo.PersistenceManagerFactory;

import javax.jdo.Query;

import javax.jdo.Transaction;

public class MyApplication {

/* persistence related constants */

final static String

persistenceManagerFactoryClass = "org.jpox.PersistenceManagerFactoryImpl",

databaseDriverName = "com.ibm.db2.jcc.DB2Driver",

connectionURL = "jdbc:db2://localhost:50000/MYDB",

username = "db2admin",

password = "db2admin",

autoCreateSchema = "false",

validateTables = "false",

validateConstraints = "false";

/**

* Main method performs Create, Update, and Delete of Employee

* Pauses are inserted inbetween so the database can be observed after each step

*/

public static void main(String[] args) {

PersistenceManager pm = createPersistenceManager();

createEmployee(pm);

pause("Employee created, waiting for user input:");

updateEmployee(pm);

pause("Employee updated, waiting for user input:");

deleteEmployee(pm);

System.out.println("Employee deleted.");

pm.close();

}

/* ADD SUBSEQUENT worker 方法S HERE */

}

 

您可以看到,这个类开始部分是一些 String 常量。这些都是 JDO 在持久存储数据时所需的参数。其中很多常量看上去应该比较熟悉,它们都是为使用 type 4 JDBC 驱动程序连接到 DB2 而需要的东西。如果您对此感到陌生,那么请参阅本文 参考资料 一节以获得更多信息。注意,这里假设 DB2 用户名和密码都是 db2admin。

最后三个字符串(autoCreateSchema、validateTables 和 validateConstraints)是 JPOX 特有的,在接下来实际创建 PersistenceManager 的代码中会讲到这一点。

main 方法简单地将每个操作委托给辅助(worker)方法。每个辅助方法创建它自己的事务,并处理任何错误检查。

createPersistenceManager() 辅助方法

让我们来看第一个辅助方法,该方法创建 PersistenceManager。在 MyApplication.java 中附加下面这个方法:

清单 6. createPersistenceManager()

/**

* Creates a PersistenceManager to create transactions

* and to persist JDO persistence compatible objects

*/

private static PersistenceManager createPersistenceManager()

{

Properties properties = new Properties();

properties.setProperty("javax.jdo.PersistenceManagerFactoryClass",

persistenceManagerFactoryClass);

properties.setProperty("javax.jdo.option.ConnectionDriverName", databaseDriverName);

properties.setProperty("javax.jdo.option.ConnectionURL",connectionURL);

properties.setProperty("javax.jdo.option.ConnectionUserName",username);

properties.setProperty("javax.jdo.option.ConnectionPassword",password);

properties.setProperty("org.jpox.autoCreateSchema",autoCreateSchema);

properties.setProperty("org.jpox.validateTables",validateTables);

properties.setProperty("org.jpox.validateConstraints",validateConstraints);

PersistenceManagerFactory pmf =

JDOHelper.getPersistenceManagerFactory(properties);

return pmf.getPersistenceManager();

}

 

注意,接收所有必要属性的工厂模式被用于创建一个 javax.jdo.PersistenceManagerFactory。接着从这个工厂中创建出一个 PersistenceManager。工厂的创建和管理器的创建都会导致较高的性能代价(类似于用 JDBC 连接到一个数据库)。因而,这两个对象应该尽可能只创建一次。

考虑 JPOX 相关属性的如下定义:

org.jpox.autoCreateSchema: 该属性指定是让 JPOX 创建数据库模式来持久存储 JDO 对象,还是让它使用一个已有的模式。在本文中,我们使用一个已有的模式,所以选择“false”。

org.jpox.validateTables: 该属性指定是否根据持久性定义对表进行验证。如果该属性被设为 true,则 JPOX 将根据 JDO 元数据对数据库表进行验证(在我们的例子中,package.jdo 是元数据文件的文件名),这样会稍微降低性能。在本文中,为了简化 Employee 对象,这个特性被禁用。

org.jpox.validateConstraints: 类似于 validateTables,不过验证发生在列一级。同样,在本文中该特性被禁用,即参数为“false”。

createEmployee() 辅助方法

接下来的辅助方法持久存储带硬编码的数据的 Employee:

清单 7. createEmployee()

/**

* worker method that persists the Employee

*/

private static void createEmployee(PersistenceManager pm) {

Employee emp = new Employee(556974421L, "John", "Doe", 2090.12F);

Transaction txn = pm.currentTransaction();

txn.begin();

pm.makePersistent(emp);

txn.commit();

}

 

从这里开始可以看到 JDO 如何允许您以面向对象的方式来处理持久性。持久存储一个对象很简单,只需调用 PersistenceManager 的 makePersistent() 方法。还应注意,这里不需要异常处理。默认情况下,JPOX 会回滚失败的事务,并在日志中作记录。如果需要更复杂的行为,那么也可以添加 try-catch-finally 块。

updateEmployee() 辅助方法

update 辅助方法持久存储对 Employee 的状态的更改:

清单 8. updateEmployee()

/**

* worker method to update the first and last name of the employee with

*/

private static void updateEmployee(PersistenceManager pm) {

Transaction txn = pm.currentTransaction();

txn.begin();

Query query = pm.newQuery

("SELECT FROM example.Employee WHERE social == 556974421");

query.setUnique(true);

Employee emp = (Employee) query.execute();

emp.setFirstName("Samuel");

emp.setLastName("Maximus");

txn.commit();

}

 

要更新雇员 John Doe 的信息,第一步是在数据存储中找到他的实例。通过构造一个 JDOQL 可以做到这一点。JDOQL 是一种用于 Java Data Objects 的查询语言,其语法类似于 SQL。注意,Query 的惟一属性是通过 setUnique(true) 设置的。这条语句规定查询只返回一条结果。这样一来,当查询执行的时候,它返回 Employee 的一个实例,而不是一个 java.util.Collection。

有了 Employee 的实例后,便只需通过传统的 setter 方法来修改其中的数据。注意,在用 setter 方法进行更新以后,不需要再次持久存储 Employee 对象。JDO 会拦截对被持久存储对象的更新,并自动持久存储状态的任何更改。但是,只有到调用了 commit() 方法之后,这些更改才永久生效。

deleteEmployee() 辅助方法

下面的方法删除 Employee 的一个实例:

清单 9. deleteEmployee()

/**

* Delete all persisted instance of Employee with social of 556974421

*/

private static void deleteEmployee(PersistenceManager pm) {

Transaction txn = pm.currentTransaction();

txn.begin();

Query query = pm.newQuery

("SELECT FROM example.Employee WHERE social == 556974421");

Collection result = (Collection) query.execute();

pm.deletePersistentAll(result);

txn.commit();

}

 

同样,为了删除 Employee 的一个实例,我们必须构造一个查询。在这个例子中,我们不使用 setUnique() 方法,所以这次查询返回的是一个 java.util.Collection (当然这里也可以使用 setUnique();但是这次我们要展示 JDO 如何使用查询结果的集合)。当查询返回集合之后,调用 PersistenceManager 的 deletePersistentAll() 方法删除集合中所有被持久存储的对象(在这个例子中只有一个这样的对象)。

pause() 辅助方法的开发

下面的方法在对雇员数据的创建、更新和删除操作之间打印文本到命令行,并等待用户输入:

清单 10. pause()

/**

* Method which blocks until the user enters a character at the command line

*/

private static void pause(String msg){

System.out.println(msg);

try {

System.in.read();

System.in.skip(System.in.available());

}

catch( IOException ioe ) {

throw new RuntimeException(ioe);

}

}

 

如前所述,代码中很多地方都穿插了对 pause 方法的调用,以便观察应用程序的代码对数据库的影响。

 

 回页首

 

编译和运行 MyApplication.java

在运行代码之前,首先应确保启动了 DB2,并且对其进行了配置,使之能接受上面代码中指定的用户(在这个例子中,用户是 db2admin)。使用下面的命令编译 /jdo-example 目录下的应用程序:

Linux 或 UNIX

               > javac -classpath classes:jdo-2.0.jar:jpox.jar -d classes src/example/MyApplication.java

Windows

               > javac -classpath classes;jdo-2.0.jar;jpox.jar -d classes src\example\MyApplication.java

使用下面的命令运行应用程序:

Linux 或 UNIX

               > java -cp classes:jdo-2.0.jar:jpox.jar:log4j.jar:db2jcc.jar:db2jcc_license_cu.jar:admin

           -Dlog4j.configuration=file:admin/log4j.properties example.MyApplication

Windows

               > java -cp classes;jdo-2.0.jar;jpox.jar;log4j.jar;db2jcc.jar;db2jcc_license_cu.jar;admin

           -Dlog4j.configuration=file:admin\log4j.properties example.MyApplication

如前所述,建议您在各个 CRUD 操作之间查看数据库。可以使用下面的 DB2 命令行处理器命令来确信对 DB2 数据的操纵符合预期:

               db2 => select * from EMPLOYEE

 

 回页首

 

出错时分析 JPOX 日志

JPOX 使用 Log4J 作为它的日志记录机制。如前所述,如果碰到问题,务必查看 /jdo-example 目录中的 jpox.log。JPOX 还有一些很好的论坛,这些论坛提供了丰富的信息,并且在您碰到难题时总会热心地给予帮助(见 参考资料)。

 

 回页首

 

在 J2EE Application Server 中使用 JPOX

在 J2EE Application Server 设置中,例如在 IBM WebSphere Application Server V6 中,支持对 JPOX 的使用。JPOX 提供了一个可以在 J2EE 设置中使用的连接器功能。在 IBM WebSphere Application Server V6 中对 JPOX 的使用超出了本文的范围。不过,在紧随本文的文章中会谈到这个话题。

 

 回页首

 

结束语

这篇介绍性的文章应该足以让您初识 JDO 2.0 的 JPOX 实现。JPOX 之所以如此流行,是因为它的语法易于使用,使得 Java 程序员无需陷入错综复杂的数据库编程的泥潭。JPOX 已经被 Sun 选作 JDO 2.0 规范的参考实现。由于 JPOX 是 JDO 标准 API 的一种实现,如果您确定 JPOX 不适合自己的需求,那么可以很轻松地换用另一种 JDO 实现来持久存储对象。

 

 回页首

 

参考资料

您可以参阅本文在 developerWorks 全球站点上的 英文原文。

Sun JDO 2.0 规范。http://www.jcp.org/en/jsr/detail?id=243

JDO 2.0 API javadocs。http://www.jpox.org/docs/1_1/javadocs/JDO2/index.html

JPOX 1.1 文档。http://www.jpox.org/docs/1_1/index.html

JPOX 论坛。 http://www.jpox.org/servlet/forum/index

JPOX 1.1 教程。 http://www.jpox.org/docs/1_1/tutorials/tutorial.html

DB2 JDBC 驱动程序信息。 DB2 UDB for Linux, UNIX 和 Windows 中的 Java 开发概述: V8.1 更新版

 

 回页首

 

作者简介

 

 Robert R. Peterson 是 IBM 软件服务部 WebSphere Enablement Team 的成员。他的工作是确保 WebSphere 系列产品能为 IBM 的客户带来尽可能大的价值。Robert 是一名颇有成就的发明家,也是 WebSphere Application Server V6: Performance and Scalability 的作者之一。他是著名的 IBM 天才孵化计划的前成员之一,拥有佛罗里达大学的计算机工程硕士学位。

 

 

 Kulvir Singh Bhogal 是一名 IBM AIM Services 顾问,在全美国的客户站点上设计和实现以 Java 为中心的解决方案。

 

 

 回页首

 

对本文的评价

 

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

建议?

 

 

 

 回页首

 

 

 

    关于 IBM      隐私条约      联系 IBM

 

yahoo.com邮箱中难以屏蔽的info@***.com形式的垃圾邮件

  我申请了yahoo.com的邮箱,可是,有大量的形如info@***.com的邮件在yahoo.com的邮箱中无法屏蔽。“***”代表几个随机的字符。这可能是一个邮件群发机的杰作。

  附大家的讨论:

Subject: RE: [UMLChina] 我也不知道怎么这么多垃圾邮件?

   

是不是有很多像info@dfsde.com这样类似的垃圾邮件?这可能不是umlchina讨论组能够解决的问题?也许是所有免费yahoo邮箱的通病也不一定?我不知道在免费的yahoo邮箱中怎么样过滤掉这种info@***.com的邮件。有谁知道了说一下。

"Geo.J.Liu (itms.cn2.Newegg)" wrote: 顶,强烈支持一下

--------------------------------------------------------------------------------

From: UMLChina@yahoogroups.com [mailto:UMLChina@yahoogroups.com] On Behalf Of 平 罗

Sent: Monday, February 06, 2006 12:15 PM

To: UMLChina@yahoogroups.com

Subject: [UMLChina] 我也不知道怎么这么多垃圾邮件?

不是加强管理,而是一要严加管理,我不知道管理员都干什么去了.

大家都呼吁一下:

“留言板”被“留言本群发”软件盯上了

  这位老兄总是不遗余力的隔几天就在网上发一次他的广告,也许还是无人值守的自动发送。内容就是卖自动发帖机等等。

  估计留言本群发是以“留言”为关键字搜索的。我来把它改一下,看情况如何。

  也许我该接受这位仁兄的提示,到网上找一个免费的群发机来试试,宣传一下学习日记,加点人气?

roller提供下载的源程序不完整?

  今天开始上班了,我把roller的源文件导入到eclipse2.1中,准备好好学习一下其中的rss部分。但是,我发现编译不能通过,报告缺少了不少*.java文件,例如:有一个文件org.roller.presentation.website.formbeans.UserAdminForm.java,其中引入了文件org.roller.presentation.forms.UserForm.java,但是后者在源码包中根本就没有。还有太多的类似情况,不知是怎么一回事?

  我再把svn仓库中的源文件下载下来看看。svn地址:http://svn.apache.org/viewcvs.cgi/incubator/roller

有点想念孩子了

  大年初二把孩子送到她外婆乡下玩,打算等孩子开校再去接她。到今天6天,现在有点想念她了。跟她打电话,问她什么时候回来,她说:今天。是不是孩子也想回来了,该不该把她接回来了?

(转帖)Struts-menu源码分析

(转自:http://champion.ewuxi.com/old/opensource/struts-new/strutsmenu.htm)

Struts-menu源码分析

        好的代码读起来让人如饮醍醐,读完以后神清气爽。如果你想提高你的编程水平,如果你想提高你的设计能力,如果你也想成为大师,那么就去阅读代码吧。以本人十几年来的编程经验,阅读代码能让你得到的比阅读文章(那怕是大师的文章)得到的更多。优秀而且实用的代码有很多,比如Junit,比如Jive,比如petStore,甚至是tomcat的Example、Log4j的Example。

        一段广告完毕,下面就为大家分析一下struts-menu的源码,作为送给大家的圣诞礼物吧。Struts-Menu也来自一位大师的作品, Matt Raible。有很多优秀的作品,比如使用struts和hibernate的struts-resume。官方网站是http://raibledesigns.com/wiki/Wiki.jsp?page=Main。Struts-Menu的最新版本是2.1。功能是使用struts技术,构建树形菜单。应该说是一个非常实用的技术,极大的方便了广大的开发人员。与此同时,个人认为它的作用还不止于些。比如,同时它也是一个使用Maven和velocity的一个很好的例子。

        首先,我们去看一下它的效果。http://www.raibledesigns.com/struts-menu/。可以看到,如此丰富多彩的菜单效果,都是在演示一个配置文件里的内容。这是一个非常好的数据与表示相分离的实现。我们打开它的源码来看。首先看一下它的包图

共有五个包,其中menu自然是完成数据组织功能,是核心之一,displayer是显示方式包,完成数据显示大部分功能。也是核心之一。taglib意义明显。example自然是一些example。util是读取资源文件的包。因些,我们重点研究的包只有三个menu,displayer和taglib。

首先我们来看menu包的类图

首先是MenuPlugIn这个类。这个类的功能很明显,就是一个struts的plug-in。可以看到,它只有一个参数menuConfig,就是menu的配置文件路径。果然,在struts-conf文件中有这么一段

  <!-- ========== Plug Ins Configuration ================================== -->

<plug-in className="net.sf.navigator.menu.MenuPlugIn">

<set-property property="menuConfig" value="/WEB-INF/menu-config.xml"/>

</plug-in>

 

 

说明配置文件来自于/WEB-INF/menu-config.xml,当然,我们可以找到相应路径下找到这个文件。如果你以前没有做过struts的plug-in,现在该知道怎么做了吧,就这么简单。通过阅读初始化函数,知道它的功能就是调用MenuRepository来建立菜单。因此。我们知道MenuRepository必然是一个组织管理管理菜单的组织类。

public void init(ActionServlet servlet, ModuleConfig config)

throws ServletException {

if (log.isDebugEnabled()) {

log.debug("Starting struts-menu initialization");

}

this.servlet = servlet;

repository = new MenuRepository();

repository.setLoadParam(menuConfig);

repository.setServlet(servlet);

try {

repository.load();

servlet.getServletContext().setAttribute(

MenuRepository.MENU_REPOSITORY_KEY,

repository);

if (log.isDebugEnabled()) {

log.debug("struts-menu initialization successfull");

}

} catch (LoadableResourceException lre) {

throw new ServletException(

"Failure initializing struts-menu: " + lre.getMessage());

}

}

 

打开MenuRepository类,我们可以看到这个类也很简单,不过已经有少可以学习的了。首先是FastHashMap,可以看到,这个类里有三个FastHashMap。顾名思议,是快速HashMap了,再看一下,它来自org.apache.commons.collections.FastHashMap;。看到org.apache.commons这个著名的包了?如果你以前从没使用过它,那么建议你花上一段时间去研究使用它,我保证物有所值。

protected FastHashMap menus = new FastHashMap();

protected FastHashMap displayers = new FastHashMap();

protected FastHashMap templates = new FastHashMap();

接下来我们看到log的定义。对了,log,调试的核心之一。而下面这一句则是commons log的最常用的使用方法。快快让你的程序使用上commons log吧,第一,它功能强大,第二,它使用简单,就是这么简单。

private Log log = LogFactory.getLog(getClass().getName());

下面看一个的函数

 protected Digester initDigester() {

        Digester digester = new Digester();

        digester.setClassLoader(Thread.currentThread().getContextClassLoader());

        digester.push(this);

        //digester.setDebug(getDebug());

        // 1

        digester.addObjectCreate("MenuConfig/Menus/Menu",

            "net.sf.navigator.menu.MenuComponent", "type");

        digester.addSetProperties("MenuConfig/Menus/Menu");

        digester.addSetNext("MenuConfig/Menus/Menu", "addMenu");

        // 2

        digester.addObjectCreate("MenuConfig/Menus/Menu/Item",

            "net.sf.navigator.menu.MenuComponent", "type");

        digester.addSetProperties("MenuConfig/Menus/Menu/Item");

        digester.addSetNext("MenuConfig/Menus/Menu/Item", "addMenuComponent",

            "net.sf.navigator.menu.MenuComponent");

        // 3       

        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item",

            "net.sf.navigator.menu.MenuComponent", "type");

        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item");

        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item",

            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 4

        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item",

            "net.sf.navigator.menu.MenuComponent", "type");

        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item");

        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item",

            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 5

        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",

            "net.sf.navigator.menu.MenuComponent", "type");

        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");

        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",

            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 6

        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",

            "net.sf.navigator.menu.MenuComponent", "type");

        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");

        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",

            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 7

        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",

            "net.sf.navigator.menu.MenuComponent", "type");

        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");

        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",

            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        digester.addObjectCreate("MenuConfig/Displayers/Displayer",

            "net.sf.navigator.displayer.MenuDisplayerMapping", "mapping");

        digester.addSetProperties("MenuConfig/Displayers/Displayer");

        digester.addSetNext("MenuConfig/Displayers/Displayer",

            "addMenuDisplayerMapping",

            "net.sf.navigator.displayer.MenuDisplayerMapping");

        digester.addSetProperty("MenuConfig/Displayers/Displayer/SetProperty",

            "property", "value");

           

        return digester;

    }

 

这里又是一个经典,digester,Digester的使用,如果你需要读一个XML配置文件,并且不想与DOM直接打交道的话,Digester将是一个很好的选择。实际上我们看到load函数调用一句 digester.parse(input);就已经把menu-config.xml建立到内存里了,就这么简单。如果你想要初始化你的系统,这种方法是不是可以学习呢?"工欲善其事,必先利其器"。我们可以看到Raible是怎么样利用现有的工具来减轻开发量的。

由于MenuRepository举重若轻的初始化过程,甚至都没有让我们看到树形结构是怎么建立到内存里去的。不过不要着急,类图给我们了明示。

看到MenuBase类了吗?对了,看名字就知道是一个Menu的基类。可以看到,它是一个简单的JavaBean。而且相信它的每个属性大家根据名字也能猜出来。所以重点讲解是MenuComponent,一个简化的 "Composite"模式。

如上图所示。由于此处的Leaf没有任何方法,只有属性。因此Leaf和Composite收缩成了一个MenuComponent类。大家都知道,Composite模式是实现树形结构最好的方法。如果你以前没有机会实现或者没有从Composite模式得到好处,那么,从这里看一下用Composite模式得到的好处。首先看它的简单,MenuComponet的实际代码很少,加起来不到十行。

public void addMenuComponent(MenuComponent menuComponent) {

        menuComponents.add(menuComponent);

        menuComponent.setParent(this);

        if ((menuComponent.getName() == null) ||

                (menuComponent.getName().equals(""))) {

            menuComponent.setName(this.name + menuComponents.size());

        }

    }

    public MenuComponent[] getMenuComponents() {

        MenuComponent[] menus =

            (MenuComponent[]) menuComponents.toArray(_menuComponent);

        return menus;

    }

 

如果你用十行来实现一个树型结构(并且还是通用的),你愿不愿意?就是通过简单的这么一些代码,实现的在内存中建立树型结构的目标。

下面我们来看DispLay包,这个包的功能也是很清楚的,就是用来显示啦。这个包的类图非常漂亮,遗憾的是也非常大。只能缩小了给大家看了。

从类图中可以看到一个非常极漂亮的面象对象的设计思路。通过一个接口,利用模板方法。最后具体实现树型结构的显示。其主要方法是displayComponents和display这两方法,init方法则实现了初始化的工作,读取javascript和图片等文件。displayComponents是一个迭代函数。从而可以遍历一个MenuCompont树。并将其显示出来。

应该说,Menu包是一个M层,而Dispplya包是一个view层,而加上TagLib包,就实现了MVC的完整结构。

两个Tag类很清楚,首先我们从怎么使用它来看它们实现的功能

<menu:useMenuDisplayer name="ListMenu"

bundle="org.apache.struts.action.MESSAGE">

        <menu:displayMenu name="ToDoListMenuFile"/>

        <menu:displayMenu name="ToDoListMenuEdit"/>

        <menu:displayMenu name="CaseDetailMenuCase"/>

        <menu:displayMenu name="Standalone"/>

</menu:useMenuDisplayer>

显而易见。useMenuDisplayer这个类是实现使用哪一种显示方式。在menu-config里我们看到ListMenu的定义

<Displayer name="ListMenu"

type="net.sf.navigator.displayer.ListMenuDisplayer"/>

displayMenu则是取得一菜单,并将其显示出来,同样在menu-config也能找到。

<Menu  name="ToDoListMenuEdit"  title="EDIT">         <Item  name="TDLselect" title="SELECT_ALL"       image="images/select-all.png"                    location="index.jsp" width="100" />         <Item  name="TDLprefs"  title="USER_PREFERENCES" image="images/prefs.png"                    location="index.jsp" width="150" />         <Item  title="Action Test" action="setPermissions?displayer=${displayer}"/></Menu>

 

查看 DisplayMenu的代码,可以看到。它完成的功能只是从context里取得MenuComponent对象,然后通过 displayer.display(menu);把它交给一个MenuDisplayer的实例来负责画出来。

因此,Control层很好的完成了控制的功能。

综上所述。通过这样一个优美的设计,把各个功能都他离开来了。如果我们需要增加一种显示方式,只要继承MenuDisplayer或它的一个子类,然后写出我们的方法,而不需要修改系统的其他部分。同样的,如果我们的菜单不准备存放在ServletContext而准备存放在比如Session里了,那么我们也只需要修改control部分和生成部分(即MenuRepository)部分。而不影响Display部分。

OK,对struts-menu的介绍结束了,下一篇文章将是如果使用struts-menu和数据库技术动态生成菜单了。请大家继续关注我的网站。

 

 

紫龙,于12/22/2003 16:45:09

蓝色天空版权所有

 

昨天一位长者劝我不要在学习日记上深入下去

  他的意思是等我做出来了,别人比这个完善太多的东西已经做出来了。你花10天半个月才能解决的问题,别人也许10分钟就解决了。

  他的话不无道理。但至少现在我对这个系统的进一步完善还有兴趣。做我内心想做的事,就算是白做了也没有关系。世上一事无成的人多了,我就做一个一事无成的人也何尝不可?

  对于学习日记的开发,我已经付出了太多,我不会轻易放弃的。

反映给argouml的三个bug不能在0.20版本解决

  今天下午,花了1个小时收邮件,主要看了这段时间积累的argouml邮件列表里的邮件,其中三封邮件告知我提交的三个bug没有在0.20版本中得到解决。看来,argouml小组目前着重在argouml基础构架的改革,而对这些应用性的问题还没有来得及去做。这三个问题分别是:

1、  [Issue 3836] Wrong fill color for states  Sat Jan 28, 2006 2k

http://argouml.tigris.org/issues/show_bug.cgi?id=3836

2、 [Issue 3777] import description is error in an ArgoUML generated *.java file  Sat Jan 28, 2006 2k

http://argouml.tigris.org/issues/show_bug.cgi?id=3777

3、 [Issue 3356] Multiplicity not preserved in roundtrip engineering 

http://argouml.tigris.org/issues/show_bug.cgi?id=3356