- 浏览: 19393 次
- 性别:
文章分类
最新评论
本文将主要介绍目前流行的JDO规范的应用开发过程。为简单起见,将会使用JSP2.0来进行页面部分的开发,因为其JSP EL可以大大简化页面的开发,节省调试时间,并增加页面的可读性。
以前曾经在CSDN上发表过一篇关于用JDO开发数据库应用的文章,那只是基于数据库的简单的文本模式的程序,仅仅是为了表达清楚JDO开发的原理和基本过程,现在这篇文章,将更加注重实用性,向各位感兴趣的读者详细介绍用JDO来开发目前流行的基于Web的三层结构数据库应用的开发过程。
在开始开发之前,我们需要先建立开发环境。这里先说明一下开发用到的第三方产品。
1 系统环境
1.1 MySQL及其JDBC驱动
我们开发的Web应用是面向数据库的,当然需要一个底层的数据库支持。我们这里使用免费的数据库产品MySQL作为底层的关系数据库。
先到{www.mysql.com}上,下载目前最新的4.0.16版本,我们下载那个19M大小的不带安装程序的ZIP包,我们将这个包解到C:\目录下,形成一个C:\ mysql-4.0.16目录(以后就用<mysql>来代表这个目录),然后运行<mysql>/bin/winMySQLadmin.exe,这样就将MySQL服务安装到本机上并启动就绪了。
接下来的开发是基于Java的,我们还需要同样在www.mysql.com上下载最新的JDBC驱动:3.0.9版。我们将下载下来的ZIP包解开,其中的mysql-connector-java-3.0.9-stable\ 目录中的mysql-connector-java-3.0.9-stable-bin.jar文件就是我们需要的JDBC驱动jar文件了。为了方便,可以将其改名为mysql.jar文件,并放到某个目录中以备使用。
为了测试MySQL JDBC的连接,可以去下载一个专门面向JDBC的数据库操纵工具:SquirrelSQL,它的网址是:{http://squirrel-sql.sourceforge.net}。这是一个免费的软件。注意连接mysql的时候有两个问题:
1. 数据库直接采用MySQL安装时自带的“test”库即可,没有用户名和密码。有些自定义的MySQL安装会将用户名设成“root”或“sa”,如果空用户名连不通,可以试试“root”或“sa”。
2. 为了能够使用中文,需要在连接MySQL的JDBC URL中设定使用GBK字符集的参数:
jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK
1.2 JDO与JDOGenie产品
JDO作为我们开发Web应用的数据对象包装层,是本篇文章的核心。为尽可能简单和最快地配置起来JDO环境,我们采用目前最简单易上手的JDOGenie作为JDO Implementation。除了实现标准的JDO规范之外,这个产品还有以下特点与我们的开发过程有关:
1. 集成的图形配置界面workbench,基本上可以实现所有配置功能
2. 在集合框架上面,支持java.util.List。这是一个可选的JDO特性。
3. 支持自动维护的双向关系。这也是JDO2.0将要加入的特性,目前还没有其他JDO产品能够做到。
4. 支持Ant集成,可以在其集成配置界面中运行Ant脚本对源代码进行编译、类增强等工作
5. 支持查询结果集的size()方法,返回准确的符合条件的总记录数。(这个方法在一些JDO产品中将返回0或Integer.MAX_VALUE,这是JDO规范允许的。)
我们先到JDOGenie的网站{www.jdogenie.com}上下载目前的最新版:2.2.0beta7,是一个ZIP包,解开到一个目录中就可以使用。我们假定这个解开成了C:\jdogenie-2.2.0beta7\目录,以后我们用<jdogenie>来代替C:\jdogenie-2.2.0beta7\目录。
另外,在下载JDOGenie后还需要申请一个试用的License,根据页面的提示,我们需要填写自己的邮箱,和一些其它信息,提交申请后,会一个封带附件的邮件发过来,这个附件就是试用License,文件名是:jdogenie.license,注意这个附件文件一定要用邮件客户端程序(如Outlook Express或Foxmail)来保存,很多网友在网易的邮箱mail.163.com网站上通过IE浏览器保存这个附件时,这个文件的内容会被改变,不能使用。
在运行JDOGenie之前,我们需要先在系统中安装好JDK,推荐最新的JDK1.4.2。安装好JDK后,要设置一个JDOGenie需要的环境变量:JAVA_HOME,它指向安装好的JDK的根目录,比如:C:\JDK1.4.2\。这个变量在下面将要提到的JSP服务器Resin中也需要。
设置好JDK后,我们直接运行<jdogenie>\workbench.bat,就进入了JDOGenie的集成配置环境,如下图:
简单介绍一下这个界面:
1. 顶部是菜单和工具条
2. 左边是基于JDO开发的几个工作区(不同职责的人员可能使用不同的工作区),依次是:
a) 树形图
这里可以看到类及类的内部属性,方便设置。不过一般不大用
b) UML类图区
这里面可以显示数据类之间的关系结构图
c) 类及类属性列表区,也是最主要的工作区
这里列出本项目中用到的数据类及其属性的各种JDO相关配置,主要的配置工作也在这个区中完成
d) 数据库创建区
用于将对象模型转化为相应的数据库建表的SQL,并可生成数据库结构
e) 查询区,应用投入运行后做维护时很重要的工作区
可以在这里进行JDOQL查询,并可对查询结果集中的对象进行属性改动,提交后,数据库中的数据会更新成改动后的数据。
也可以在主这里进行SQL操作,比如某个类加了一个属性后在这里执行一个对该表添加字段的SQL操作。
f) 远程控制台
主要是连接远程的JDOGenie引擎,并观察服务器运行状况的工作区,可以查看哪些JDOQL以及生成的SQL运行了多少次,平均占用时间是多少,以便进行相应的优化(如添加索引、改进查询代码等)
3. 底部是状态条,当某个操作有错误时,状态条上会以红色显示最新的错误描述,点击状态条右边第一个按钮可以查看详细的错误信息。状态条右方显示当前JDOGenie工作台占用了多少内存,如果发现太多内存占用,估计是垃圾对象太多,可以点击最右边的垃圾回收按钮进行内存释放。
1.3 Ant:Java批处理工具
作为Java开发中的批处理工具(类似C/C++中的Make工具,主要用于编译、环境设置、文件组织等等),Ant是Apache组织对Java世界最大的贡献之一,现在已经成为面向Java开发的批处理方面事实上的标准。关于Ant的介绍文章很多,在{www.CSDN.net}可以找到一大堆,这里也不多介绍了。我们先去Ant的网站{http://ant.apache.org}下载最新版:1.5.4,下载下来的是一个ZIP包,解开到某个目录下即可。这里假定解开到C:\ apache-ant-1.5.4\目录中,下面采用<ant>来代替C:\apache-ant-1.5.4目录。
注意,有些HTTP代理对{http://ant.apache.org/}首页上的一些脚本有过滤,导致有些用户可能访问这个页面时无法看到内容,这时,可以直接访问{http://ant.apache.org/bindownload.cgi} 来下载Ant1.5.4。
为了以后直接通过DOS命令行处理的方便,我们建议在系统中配置PATH环境变量,将<ant>/bin/目录加到其中,这样就可以在命令行状态直接输入“ant”来运行“<ant>/lib/ant.bat”命令了。
Ant是一个Java开发的产品,也是依赖于前面提到的JAVA_HOME环境变量的,Ant通过它找到JDK,从而运行用户编写的Ant批处理脚本。
现在Ant安装好了,我们再打开JDOGenie,配置一下与Ant的集成:选择菜单“FileàWorkbench Settings…”,进行JDOGenie的环境配置界面,我们在其中设定Ant的入口程序ant.bat的路径,如图:
这里笔者的Ant是安装在D:\BAK\ant目录下的,读者需要根据自己的实际情况设置自己的<ant>/bin/ant.bat路径。其它设置建议保持不动。
1.4 JSP与Resin服务器
为简单易懂,本文将会以JSP2.0标准来编写JSP页面,JSP2.0定义了一个非常有用的数据输出方式:JSP EL(JSP Expression Language),可以通过标准JavaBean的getXXX()/isXXX()和setXXX()来实现Java对象数据的输出,并且,JSP2.0还纳入了一套标记库:JSTL,里面包含很多常用的JSP页面处理机制,如循环、条件化显示等等。关于JSP2.0的功能介绍,也可到{www.CSDN.net}上去搜索“JSP 2.0”的相关文章。
目前JSP2.0规范还未正式出台,还处于ProposedFinalDraft-3阶段,不过很快将会正式发布。支持JSP2.0的服务器有Tomcat5、Resin3等等,出于方便、易维护的目的,我们选用Resin作JSP服务器。先到Resin网站{www.caucho.com}上下载最新的3.0.4版。下载下来的是一个ZIP包,解开到某个目录下即可使用。假定我们将包中内容解到C:\下,形成一个C:\ resin-3.0.4\目录,以后我们使用<resin>来代表“C:\ resin-3.0.4”目录。
完成以上工作后,我们进入<resin>/bin目录,运行其中的httpd.exe程序,就可以看到Resin服务器启动了。如果希望将Resin安装成为系统的一项服务,可以使用httpd.exe –install安装,并用net start resin来启动Resin服务。不过为了方便地查看Resin服务器的输出日志,我们不建议安装Resin为系统服务,而建议运行httpd.exe来直接启动Resin。Resin服务器有以下特点:
1. 自动检测WEB应用的发布和更新
通过对<resin>/webapps/下的子目录进行监测,可以动态地发布或取消某个web应用,也可以监测某个WEB应用的WEB-INF/web.xml的更新来自动重新发布该应用
2. 自动编译更新过的java文件,如果Resin检测到某个应用的WEB-INF/classes/下的java源文件被更新,则会自动编译该文件
3. 检测JSP文件的更新,如果某个JSP页面被更新过,则Resin会重新将它编译。(这个功能多数JSP服务器都有)
Resin运行起来后,我们打开Internet Explorer浏览器,输入:{http://localhost:8080/} ,就可以看到Resin的主页面,表示Resin已经成功运行,如图:
2 Web应用:论坛
在下面这两章中,我们将会经历一个论坛项目的开发和维护过程,从最初的非常简单的需求,到开发、运行,然后再经历一系列的需求变更和功能改进,来看看使用JDO是如何达到软件的快速开发与维护和应用的随需应变的。并且,还会涉及到一些性能优化和传统JDBC开发很难实现的老问题(如字符串字段长度限制等)。
2.1 准备工作
在开始开发之前,我们必须先弄清楚一些基本的概念,并按J2EE对Web应用的要求建立相应的运行环境。
2.1.1 认识JDO的开发过程 关于JDO的开发流程,以前的文章和网上一些资料也详细介绍过,这里简单地重复一下:
先编写代表数据对象的类,编译生成.class文件,再编写描述该类中每个属性的保存细节的元数据:.jdo文件,然后根据.jdo文件对.class文件进行增强(Enhance),这个数据类就可以在代码中通过JDO的API进行调用了。图示如下:
2.1.2 建立符合J2EE中WebApp规范的应用目录结构
为了减少与Resin的整合配置,我们将论坛应用直接放到<resin>/webapps/下,建立一个名为bbs的子目录。这样,就可以通过在服务器地址后面加入“/bbs/”来访问这个WEB应用了。
在bbs目录中,我们按J2EE规范建立一个“WEB-INF”子目录,然后在WEB-INF中再建立“classes”和“lib”两个子目录,将MySQL的JDBC驱动mysql.jar和JDOGenie的支持包<jdogenie>/lib/目录下的jdogenie.jar和jta.jar两个文件复制到这个<resin>/webapps/bbs/WEB-INF/lib目录中,作为本应用依赖的第三方组件,并将<jdogenie>/license/jdogenie.license文件复制到<resin>/webapps/bbs/WEB-INF/classes/目录下。这些配置就是将jdogenie整合到bbs应用中。
接下来,我们开始确定bbs应用的Java类的包结构。简单起见,我们将源文件都放到“jdobbs”包中,也就是说,这个应用中涉及的Java源代码都会以“package jdobbs;”开头。
这些目录都建好以后,我们得到下面的目录结构:
我们先在bbs目录下放一个index.jsp文件:
<title>JDO BBS on ${pageContext.request.serverName}</title>
您现在的位置:<b>JDO BBS 首页</b>
<h3>欢迎访问JDOBBS!</h3>
<p>本系统采用JDO + JSP 2.0 完成<p>
<p>您的浏览器类型是:${header['user-agent']}
<p>您的IP地址是:${pageContext.request.remoteAddr}
然后将Resin启动,看看效果:
好!现在我们的第一个JSP页面就已经完成了。该页面代码中用到的“${pageContext.request.serverName} ”、“${header['user-agent']}”和“${pageContext.request.remoteAddr}”都是JSP2.0中定义的JSP EL表达式。
2.1.3 一个处理JDO资源的工具类:jdobbs.Sys
我们先来写一个非常关键的工具类:jdobbs.Sys,这个类负责处理一些基本的JDO 存储管理器获取、资源释放、线程同步方面的事务。关于其中的每个细节,都可以用一大篇文章来说明(详情见{http://www.jdocentral.com/forums/index.php?s=8bf08748ea2b95fe52e0d25c7a489732&act=SF&f=11}中的相关讨论),这里为了简单起见,只简单地描述一下这个工具类的目标: 1. 保证每个JSP页面请求的服务端线程中所有的业务逻辑方法共享同一个javax.jdo.PersistenceManager,除非在处理过程中间显式地关闭当前的PersistenceManager。这将采用java.lang.ThreadLocal接口实现
2. 保证每个页面请求结束后,处理请求过程中涉及的JDO资源全部被释放(即关闭用到的PersistenceManager)。这将采用javax.servlet.Filter接口实现
我们来看看下面的Sys.java源码:
package jdobbs;
import javax.jdo.*;
import javax.servlet.*;
import java.io.*;
import java.util.*;
public class Sys extends ThreadLocal implements Filter {
/** 获取PersistenceManagerFactory */
public static PersistenceManagerFactory pmf() {
if(pmf == null) {
Properties p = new Properties();
InputStream is = Sys.class.getResourceAsStream("/jdobbs.jdogenie");
try {
p.load(is);
} catch(IOException ex) {
throw new RuntimeException("初始化PersistenceManagerFactory时配置文件读取失败!",ex);
}
pmf = JDOHelper.getPersistenceManagerFactory(p);
}
return pmf;
}
/** 获取PersistenceManager
* 通过使用ThreadLocal对象,使同一线程中每次调用本方法,都会返回同一PM对象。
*/
public static PersistenceManager pm() {
if(threadLocal == null) {
threadLocal = new Sys();
}
return (PersistenceManager)threadLocal.get();
}
/** 用于释放本次线程中使用到的JDO资源
* @return 是否有未完成的Transaction,便于后台日志区别记录
*/
public static boolean cleanupResource() {
return threadLocal != null && threadLocal.cleanup();
}
private static PersistenceManagerFactory pmf;
//============= 以下是ThreadLocal的相关方法==============
private static Sys threadLocal; //一个用于保证同一线程只访问同一PM对象的控制器
/**
* 获取一个与当前线程相关的PersistenceManager
* @return 一个PersistenceManager类型的对象,调用者需要强制转换一下类型。
*/
public Object get() {
PersistenceManager pm = (PersistenceManager)super.get();
if(pm == null || pm.isClosed()) {
pm = pmf().getPersistenceManager();
set(pm);
}
return pm;
}
/**
* 释放所有与本线程相关的JDO资源
* @return 是否有未完成的Transaction,便于后台日志区别记录
*/
public boolean cleanup(){
PersistenceManager pm = (PersistenceManager)super.get();
if(pm == null) return false;
if(pm.isClosed()) {
set(null); return false;
}
boolean tsActive = false;
try {
Transaction ts = pm.currentTransaction();
tsActive = ts.isActive();
if(tsActive) ts.rollback();
} finally {
set(null);
pm.close();
}
return tsActive;
}
//============= 以上是ThreadLocal的相关方法==============
//============= 以下是JSP Filter的相关方法==============
public void init(FilterConfig p0) throws ServletException {}
public void destroy() {}
/** JSP页面(Servlet)处理的过滤器方法,用于JSP执行完毕后检测并释放用到的JDO资源 */
public void doFilter(ServletRequest req, ServletResponse res, FilterChain
fc) throws ServletException,IOException {
try {
fc.doFilter(req,res);
} finally {
cleanupResource();
}
}
//============= 以上是JSP Filter的相关方法==============
}
简单地说,这个类提供了一个静态的pm()方法,可以获取一个JDO的存储管理器PersistenceManager,这个方法将是以后的代码中用到最多的方法。
其中的Sys. pmf()用于获取PersistenceManagerFactory,这个方法中用到了一个配置文件:jdobbs.jdogenie,这个文件是我们下面将要生成的JDOGenie的配置文件,也就是将在workbench中产生应用配置文件,其中包括数据库连接地址、用户、密码信息,以及一些性能调节的设置。这个配置文件jdobbs.jdogenie将被放置在bbs/WEB-INF/classes/目录下,以便程序读取。
这个Sys类使用了Servlet的过滤接口来保证每个JSP执行完毕后相关的JDO资源得以释放。因此,编译这个类时,一定要将Servlet2.4的支持包包含在CLASSPATH中,这个包即是“<resin>/lib/j2sdk-24.jar”文件。为了以后编译和进行其它工作的方便,我们编写一个Ant脚本作为执行各种批处理(包括编译)的工具。
2.1.4 配置JSP过滤器
为了让前面的JSP Filter生效,我们需要将其配置到我们的Web应用中去,准确地说,就是创建(或更改)bbs/WEB-INF/web.xml文件,我们更改该文件后,看看它的源代码:web.xml
<web-app>
<filter>
<filter-name>JDO_Resource_Cleaner</filter-name>
<filter-class>jdobbs.Sys</filter-class>
</filter>
<filter-mapping>
<filter-name>JDO_Resource_Cleaner</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
这段内容保证了每个JSP页面请求处理完后,Sys.cleanup()方法将得到执行,以检测并清除用到的JDO资源。
2.1.5 Ant脚本
按Ant的标准,我们编写一个名为“build.xml”的脚本,并且,为了配置上的方便,我们将这个文件放到bbs/WEB-INF/目录下。
我们看看这个Ant脚本的源代码:build.xml
<project name="jdobbs" default="enhance">
<property name="resin" location="D:/resin-3.0.4" />
<path id="cp">
<pathelement path="${resin}/lib/jsdk-24.jar" />
<pathelement path="classes" />
<fileset dir="lib" includes="*.jar" />
</path>
<target name="compile">
<javac srcdir="classes" classpathref="cp" debug="true" />
</target>
<target name="enhance" depends="compile" description="Enhance the compiled classes">
<taskdef resource="jdogenie.tasks" classpathref="cp" />
<jdo-enhance project="${ant.project.name}.jdogenie" outputdir="classes" />
</target>
</project>
将这个文件放到bbs/WEB-INF/目录下后,我们开启一个DOS命令行窗口,并进入该目录,运行一下:“ant compile”,将会看到如下结果:
D:\resin-3.0.4\webapps\bbs\WEB-INF>ant compile
Buildfile: build.xml
compile:
[javac] Compiling 1 source file to D:\resin-3.0.4\webapps\bbs\WEB-INF\classes
BUILD SUCCESSFUL
Total time: 2 seconds
D:\resin-3.0.4\webapps\bbs\WEB-INF>
这个结果表示我们的Ant脚本已经成功运行了。如果你看到的结果是:
'ant' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
那就说明你的Ant没有配置好(比如你开了DOS窗口后才去更改系统的PATH环境变量),这方面的问题参见前面的Ant安装与配置进行解决。
脚本的内容这里不一一解释了,请参考Ant的帮助。这里只说明一下:“enhance”批处理中通过jdogenie.jar中的Ant任务定义,使用了一个“jdo-enhance”任务,这个任务就是用来增强类代码的。
好了,进行了以上这些准备工作,为确信以上的工作都没有出现错误,我们再次从浏览器访问一下首页,结果与最开始的首页完全一样,符合预期。
接下来,我们就可以开始进行真正的JDO分析和设计了。
2.2 功能需求
我们最初的需求很简单,就是让网友可以在这个系统中交流,以发贴、回复的形式,简单地看,可以说这是一个留言本系统。我们要达到以下目标: 1. 网友进入主页时,系统列出目前所有的主题,按时间顺序从近到远排列。
2. 网友可以在主页下方的发贴表单中发表新主题,包括标题和内容
3. 其它网友在主页的贴子列表中点击某个标题可以阅读这个主题的详细内容,包括所有的回贴内容。所有回贴按照时间顺序排列在主题主贴后面
4. 可以在主题详细内容页面尾部的回贴表单中回复这个主题
5. 每个网友发表主题或贴子时,系统需要记录该贴子发表时的客户端IP地址
6. 系统提供一个高级搜索功能,让网友可以根据时间、主题标题、内容或回复内容、IP搜索主题
2.3 分析、数据对象模型与JDO集成
通过对需求的分析,我们发现,系统中需要存储的信息就是网友提交的主题或者回复。主题和回复都需要记录发贴的网友的IP地址,都有文字内容,但二者又稍有不同,主题可以有一个标题,而回复不需要(目的是为了方便回贴),另外,一个回复贴子一定是针对一个主题的。
从面向对象的角度出发,我们将以上的数据包装成为下列的对象模型:
(后面我们会看到这个图是如何弄出来的)
2.3.1 编写数据类源码
我们根据贴子的共性,编写一个基类:Post(贴子),代表所有的主题和回复,具有贴子所具备的几个基本属性。而主题类我们用Topic来表示,它从Post继承;回复类我们用Reply来表示,它有一个属性:topic,表示所回复的主题。主题和回复之间具有一对多关系,体现在Topic.replies和Reply.topic属性上。为了JSP2.0中的JSP EL的访问,我们给每个属性加上getter/setter(很多IDE工具都提供自动添加getter/setter的功能)。
下面看看这三个文件的源代码(注意,都放在WEB-INF/classes/jdobbs/目录下):
贴子类Post.java:
package jdobbs;
import java.util.*;
/**
* 网友所发表的贴子,包括话题和回复
*/
public class Post {
/** 贴子内容 */
String content;
/** 贴子内容的长度 */
int length;
/** 发贴时客户机器的IP地址 */
String ip;
/** 发表本贴的时间 */
Date postTime;
public String getContent() {
return content;
}
public void setContent(String value) {
content = value;
}
public int getLength() {
return length;
}
public void setLength(int value) {
length = value;
}
public String getIp() {
return ip;
}
public void setIp(String value) {
ip = value;
}
public Date getPostTime() {
return postTime;
}
public void setPostTime(Date value) {
postTime = value;
}
}
主题类Topic.java:
package jdobbs;
import java.util.*;
/**
* 话题
*/
public class Topic extends Post {
/** 标题 */
String title;
/** 回复数 */
int replyCount;
/** 所有回复 */
List replies;
/** 最后更新,即最后被回复的时间 */
Date lastUpdate;
public String getTitle() {
return title;
}
public void setTitle(String value) {
title = value;
}
public int getReplyCount() {
return replyCount;
}
public void setReplyCount(int value) {
replyCount = value;
}
public List getReplies() {
return replies;
}
public void setReplies(List value) {
replies = value;
}
public Date getLastUpdate() {
return lastUpdate;
}
public void setLastUpdate(Date value) {
lastUpdate = value;
}
}
回复类Reply.java:
package jdobbs;
/**
* 回复
*/
public class Reply extends Post {
/** 所回复的话题 */
Topic topic;
public Topic getTopic() {
return topic;
}
public void setTopic(Topic value) {
topic = value;
}
}
为了在下面使用JDOGenie工具来配置JDO环境,我们先在build.xml所在目录中运行一下编译任务:ant compile,编译这几个类。
2.3.2 配置JDOGenie,自动创建system.jdo元数据文件、自动建数据库,等等
接下来,我们用JDOGenie来实现这个对象模型。注意,接下来这几步很重要!
我们进入<jdogenie>/目录,运行workbench.bat,开始我们的JDO之旅。在主窗口中,我们通过菜单“FileàNew Project…”新建一个JDOGenie项目(这个项目实际上就是JDOGenie的配置文件),在出现的项目文件对话框中,我们选择<resin>/webapps/bbs/WEB-INF/classes/目录,并在文件名输入栏中输入项目名称(也就是配置文件的名称):“jdobbs.jdogenie”,注意,请输入完整文件名。如图:
点击确定后,我们将看到一个项目配置设置的对话框。在这个配置对话框的左边列出配置项,右边则每项的配置内容。注意,带目录图标的“Project”和“Datastore”也是配置项。我们将在这个对话框中逐项地配置必须的信息:
1. 首先出现的是Project属性设置,即左边的Project项,在此项中,我们将“Non-transactional read”检查框选中,这样可以不用声明事务就直接通过存储管理器(PersistenceManager)访问数据库中的对象。如图:
2. 接下来是Ant的集成配置,目的是便于直接在工作台中调用Ant完成数据类的编译和增强。我们需要输入ant.bat和build.xml所在的路径,以便调用。如图:
当然,我们也可以总是手工地在命令行调用Ant,这只需要将上图中的“Disable Ant”选中即可。
3. CLASSPATH设置。这里需要加入数据类的根路径和MySQL的JDBC包,如图:
4. 指定Metadata文件。按照JDO规范的要求,必须用.jdo结尾的文件(也称元数据或描述符)来描述需要存储的数据类。我们选择使用CLASSPATH根目录下的system.jdo文件来描述本系统中所有需要存储的数据类。先选择配置对话框左边的“JDO Meta Data Files”,出现如下对话框:
点击按钮“New”在WEB-INF/classes/下新建一个system.jdo文件,如图:
点击“保存”按钮,回到配置对话框:
5. 在“Project”部分的剩下几个配置项主要用于性能优化等方面的调节,我们暂时不考虑。现在直接点击“Datastore”项,进入数据库连接信息配置项,如图:
点击“Choose”按钮,选择MySQL类型的数据库,然后在URL栏中输入到本机MySQL数据库的连接:“jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK”,该URL中的GBK设置保证MySQL处理中文时不会乱码。配置好后,界面如下:
现在,我们可以点击下方的“Test”按钮来检测数据库是否能连通(在此之前,请先检查一下MySQL是否已经运行)。如果不能连通,请检查前面的配置是否正确(Classpath配置中是否加入了mysql.jar,URL是否输入正确)
到此为止,“Datastore”部分剩下的几项配置都可暂不理会,直接点击最下方的“OK”按钮关闭配置对话框。这时出现的是一个空的界面,如图:
在这里,简单介绍一下JDOGenie的界面,左边是几个大按钮,代表各个方面的功能(前面已经介绍过),从上到下依次是:类树、类图、类列表、数据库结构、JDO查询、远程控制台。后面会介绍这些功能。
我们现在将前面编写的数据类添加到项目中,这个步骤将生成JDO规范中要求的.jdo元数据文件。在上面的界面的上半部分的类列表中点击右键,在弹出菜单中选择“Add Classes…”,这时系统会列出从CLASSPATH中找到的.class类文件,我们选择其中的三个:Post、Topic、Reply:
确定后,系统提示放入哪个元数据文件中,并且列出了前面创建的“system.jdo”文件。直接点击“OK”即可。我们将看到主窗口变成了下面的样子:
工具条上的第四个按钮“JDO Metadata Compile”变成了红色,表示system.jdo中的信息中有歧义,点一下这个按钮看看详细原因:
原来是系统不知道Topic类中的replies列表(List)的元素类型,在主窗口中选中Topic类,在界面下半部分的类属性列表中,在replies属性行上点击右键,选择“Field Properties…”,进入replies属性设置的对话框,在对话框中,我们需要设置两项:第一项,在“Element Type”中选择jdobbs.Reply类,表示replies列表的元素是Reply对象;第二项,在“Inverse field”中选择“topic”,表示Topic.replies与Reply.topic是一对双向关系,任何一边的改动都会影响到另一边。如图:
设置好后,点击这个对话框右上角的“”按钮关闭它,现在在主窗口中,工具条上的第四个按钮已经变成绿色()了,表示元数据已经没有问题了。我们保存一下项目:“File->Save”,现在就可以增强我们的数据类了。选择菜单“BuildàCompile & Enhance”或者点击工具条上的第五个按钮“”,我们将看到成功编译和增强的信息:
下面我们需要为我们的数据类建立相应的数据表,我们点击一下左边的大按钮“ Database schema operations…”,系统会列出当前的数据类所需要的数据表结构,这里我们只看到一个表,因为我们的三个类相互之间是继承关系,JDOGenie对继承的类用同一个表来表示。我们选择菜单“BuildàRecreate Schema”,在数据库中生成这个表结构(如果是以后改变了类,就不能再重建了,而需要在JDO查询区“Queries”中用ALTER TABLE的SQL语句来解决,否则会丢失数据):
以上的一系列配置过程,就是我们集成JDO的过程,乍一看,好象配置非常复杂,其实前面这一系列过程很快就可以完成,理解了JDO的原理后会觉得很简单。以后,这个JDOGenie的工作台workbench.bat就是我们的工作环境了。我们进一步更改类源码后,只需要在这里点击一下编译按钮“”,就可以将最新的源文件编译并增强了。如果增加了更多的数据类,也只需要在这个工作台中将新的类加入到项目中(即元数据中)即可。
最后,我们回想一下前面提到的数据类图,该图实际上也是在JDOGenie的工作台中生成的。我们点击左边的大按钮“ Class and E/R diagrams”进入类图区,在窗口中点右键,选择“Choose Classes…”,将所有的数据类都加入到图中,就会看到包含Post、Topic、Reply三个类的类图,对位置稍加调整,就可以得到前面的类图了,如图:
好了,以上所介绍的是JDOGenie的工作台图开操作界面的诸多细节,实际上,我们还没有涉及JDO开发中的核心部分:API的使用。不过相信经过以上的介绍,读者对作为一个JDO实现的JDOGenie也有了比较全面的认识了。然而,对于JDO开发来说,不同的产品有不同的配置工具和配置操作,各有特色,比如KodoJDO的数据库是可以配置为在运行时动态生成和动态与数据类模型同步的,这一点深受开发人员喜爱。可喜的是,JDOGenie很快也将加进这一非常有利于快速开发和维护的特性。
在我们开始真正的JDO开发之旅之前,我们先休息一会儿,回顾一下前面提到过的各个层次的服务器,包括数据库、JSP服务器、JDO产品等等。在本文的《开发篇》中,将给读者揭开JDO的神秘面纱,让读者看清楚开发过程中的每一个细节……
本文的版权属于笔者本人,但欢迎转载,前提是注明出处和原作者。另外,欢迎在我的专栏中查看我的另几篇文章,并提出宝贵意见!
对该文的评论 人气:2948
KevinAAA (2004-3-31 17:01:19)
写的真的很不错,值得收藏!
sqcool2000 (2004-3-10 17:28:27)
我按照说明做了,创建表的时候出现一个小问题,提示已经成功,但是就是没有表真正生成。我没有写Sys.java,是不是这里有问题呢
fkpwolf (2003-12-13 15:47:32)
写的如此详细,一个字,好!
sun2bin (2003-12-12 13:14:29)
EJB效率不足,配置、调试麻烦,适合数据重要但时间无所谓的大公司 Hibernate免费且用户多,功能也挺强,不过配置麻烦,开发也有些烦琐,而性能则不知道如何 JDO使用开发简单,维护方便,不过目前状况是各家特点不一,产品差异还较大,标准的JDO1.0功能有限。
jenny_cai (2003-12-8 13:51:59)
高手是怎样练成的,就是这样练成的。希望能解释一下各种ORM的优点和不足。ENTITY EJB,HIBERNATION,JDO,随更好?[img][/img]
以前曾经在CSDN上发表过一篇关于用JDO开发数据库应用的文章,那只是基于数据库的简单的文本模式的程序,仅仅是为了表达清楚JDO开发的原理和基本过程,现在这篇文章,将更加注重实用性,向各位感兴趣的读者详细介绍用JDO来开发目前流行的基于Web的三层结构数据库应用的开发过程。
在开始开发之前,我们需要先建立开发环境。这里先说明一下开发用到的第三方产品。
1 系统环境
1.1 MySQL及其JDBC驱动
我们开发的Web应用是面向数据库的,当然需要一个底层的数据库支持。我们这里使用免费的数据库产品MySQL作为底层的关系数据库。
先到{www.mysql.com}上,下载目前最新的4.0.16版本,我们下载那个19M大小的不带安装程序的ZIP包,我们将这个包解到C:\目录下,形成一个C:\ mysql-4.0.16目录(以后就用<mysql>来代表这个目录),然后运行<mysql>/bin/winMySQLadmin.exe,这样就将MySQL服务安装到本机上并启动就绪了。
接下来的开发是基于Java的,我们还需要同样在www.mysql.com上下载最新的JDBC驱动:3.0.9版。我们将下载下来的ZIP包解开,其中的mysql-connector-java-3.0.9-stable\ 目录中的mysql-connector-java-3.0.9-stable-bin.jar文件就是我们需要的JDBC驱动jar文件了。为了方便,可以将其改名为mysql.jar文件,并放到某个目录中以备使用。
为了测试MySQL JDBC的连接,可以去下载一个专门面向JDBC的数据库操纵工具:SquirrelSQL,它的网址是:{http://squirrel-sql.sourceforge.net}。这是一个免费的软件。注意连接mysql的时候有两个问题:
1. 数据库直接采用MySQL安装时自带的“test”库即可,没有用户名和密码。有些自定义的MySQL安装会将用户名设成“root”或“sa”,如果空用户名连不通,可以试试“root”或“sa”。
2. 为了能够使用中文,需要在连接MySQL的JDBC URL中设定使用GBK字符集的参数:
jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK
1.2 JDO与JDOGenie产品
JDO作为我们开发Web应用的数据对象包装层,是本篇文章的核心。为尽可能简单和最快地配置起来JDO环境,我们采用目前最简单易上手的JDOGenie作为JDO Implementation。除了实现标准的JDO规范之外,这个产品还有以下特点与我们的开发过程有关:
1. 集成的图形配置界面workbench,基本上可以实现所有配置功能
2. 在集合框架上面,支持java.util.List。这是一个可选的JDO特性。
3. 支持自动维护的双向关系。这也是JDO2.0将要加入的特性,目前还没有其他JDO产品能够做到。
4. 支持Ant集成,可以在其集成配置界面中运行Ant脚本对源代码进行编译、类增强等工作
5. 支持查询结果集的size()方法,返回准确的符合条件的总记录数。(这个方法在一些JDO产品中将返回0或Integer.MAX_VALUE,这是JDO规范允许的。)
我们先到JDOGenie的网站{www.jdogenie.com}上下载目前的最新版:2.2.0beta7,是一个ZIP包,解开到一个目录中就可以使用。我们假定这个解开成了C:\jdogenie-2.2.0beta7\目录,以后我们用<jdogenie>来代替C:\jdogenie-2.2.0beta7\目录。
另外,在下载JDOGenie后还需要申请一个试用的License,根据页面的提示,我们需要填写自己的邮箱,和一些其它信息,提交申请后,会一个封带附件的邮件发过来,这个附件就是试用License,文件名是:jdogenie.license,注意这个附件文件一定要用邮件客户端程序(如Outlook Express或Foxmail)来保存,很多网友在网易的邮箱mail.163.com网站上通过IE浏览器保存这个附件时,这个文件的内容会被改变,不能使用。
在运行JDOGenie之前,我们需要先在系统中安装好JDK,推荐最新的JDK1.4.2。安装好JDK后,要设置一个JDOGenie需要的环境变量:JAVA_HOME,它指向安装好的JDK的根目录,比如:C:\JDK1.4.2\。这个变量在下面将要提到的JSP服务器Resin中也需要。
设置好JDK后,我们直接运行<jdogenie>\workbench.bat,就进入了JDOGenie的集成配置环境,如下图:
简单介绍一下这个界面:
1. 顶部是菜单和工具条
2. 左边是基于JDO开发的几个工作区(不同职责的人员可能使用不同的工作区),依次是:
a) 树形图
这里可以看到类及类的内部属性,方便设置。不过一般不大用
b) UML类图区
这里面可以显示数据类之间的关系结构图
c) 类及类属性列表区,也是最主要的工作区
这里列出本项目中用到的数据类及其属性的各种JDO相关配置,主要的配置工作也在这个区中完成
d) 数据库创建区
用于将对象模型转化为相应的数据库建表的SQL,并可生成数据库结构
e) 查询区,应用投入运行后做维护时很重要的工作区
可以在这里进行JDOQL查询,并可对查询结果集中的对象进行属性改动,提交后,数据库中的数据会更新成改动后的数据。
也可以在主这里进行SQL操作,比如某个类加了一个属性后在这里执行一个对该表添加字段的SQL操作。
f) 远程控制台
主要是连接远程的JDOGenie引擎,并观察服务器运行状况的工作区,可以查看哪些JDOQL以及生成的SQL运行了多少次,平均占用时间是多少,以便进行相应的优化(如添加索引、改进查询代码等)
3. 底部是状态条,当某个操作有错误时,状态条上会以红色显示最新的错误描述,点击状态条右边第一个按钮可以查看详细的错误信息。状态条右方显示当前JDOGenie工作台占用了多少内存,如果发现太多内存占用,估计是垃圾对象太多,可以点击最右边的垃圾回收按钮进行内存释放。
1.3 Ant:Java批处理工具
作为Java开发中的批处理工具(类似C/C++中的Make工具,主要用于编译、环境设置、文件组织等等),Ant是Apache组织对Java世界最大的贡献之一,现在已经成为面向Java开发的批处理方面事实上的标准。关于Ant的介绍文章很多,在{www.CSDN.net}可以找到一大堆,这里也不多介绍了。我们先去Ant的网站{http://ant.apache.org}下载最新版:1.5.4,下载下来的是一个ZIP包,解开到某个目录下即可。这里假定解开到C:\ apache-ant-1.5.4\目录中,下面采用<ant>来代替C:\apache-ant-1.5.4目录。
注意,有些HTTP代理对{http://ant.apache.org/}首页上的一些脚本有过滤,导致有些用户可能访问这个页面时无法看到内容,这时,可以直接访问{http://ant.apache.org/bindownload.cgi} 来下载Ant1.5.4。
为了以后直接通过DOS命令行处理的方便,我们建议在系统中配置PATH环境变量,将<ant>/bin/目录加到其中,这样就可以在命令行状态直接输入“ant”来运行“<ant>/lib/ant.bat”命令了。
Ant是一个Java开发的产品,也是依赖于前面提到的JAVA_HOME环境变量的,Ant通过它找到JDK,从而运行用户编写的Ant批处理脚本。
现在Ant安装好了,我们再打开JDOGenie,配置一下与Ant的集成:选择菜单“FileàWorkbench Settings…”,进行JDOGenie的环境配置界面,我们在其中设定Ant的入口程序ant.bat的路径,如图:
这里笔者的Ant是安装在D:\BAK\ant目录下的,读者需要根据自己的实际情况设置自己的<ant>/bin/ant.bat路径。其它设置建议保持不动。
1.4 JSP与Resin服务器
为简单易懂,本文将会以JSP2.0标准来编写JSP页面,JSP2.0定义了一个非常有用的数据输出方式:JSP EL(JSP Expression Language),可以通过标准JavaBean的getXXX()/isXXX()和setXXX()来实现Java对象数据的输出,并且,JSP2.0还纳入了一套标记库:JSTL,里面包含很多常用的JSP页面处理机制,如循环、条件化显示等等。关于JSP2.0的功能介绍,也可到{www.CSDN.net}上去搜索“JSP 2.0”的相关文章。
目前JSP2.0规范还未正式出台,还处于ProposedFinalDraft-3阶段,不过很快将会正式发布。支持JSP2.0的服务器有Tomcat5、Resin3等等,出于方便、易维护的目的,我们选用Resin作JSP服务器。先到Resin网站{www.caucho.com}上下载最新的3.0.4版。下载下来的是一个ZIP包,解开到某个目录下即可使用。假定我们将包中内容解到C:\下,形成一个C:\ resin-3.0.4\目录,以后我们使用<resin>来代表“C:\ resin-3.0.4”目录。
完成以上工作后,我们进入<resin>/bin目录,运行其中的httpd.exe程序,就可以看到Resin服务器启动了。如果希望将Resin安装成为系统的一项服务,可以使用httpd.exe –install安装,并用net start resin来启动Resin服务。不过为了方便地查看Resin服务器的输出日志,我们不建议安装Resin为系统服务,而建议运行httpd.exe来直接启动Resin。Resin服务器有以下特点:
1. 自动检测WEB应用的发布和更新
通过对<resin>/webapps/下的子目录进行监测,可以动态地发布或取消某个web应用,也可以监测某个WEB应用的WEB-INF/web.xml的更新来自动重新发布该应用
2. 自动编译更新过的java文件,如果Resin检测到某个应用的WEB-INF/classes/下的java源文件被更新,则会自动编译该文件
3. 检测JSP文件的更新,如果某个JSP页面被更新过,则Resin会重新将它编译。(这个功能多数JSP服务器都有)
Resin运行起来后,我们打开Internet Explorer浏览器,输入:{http://localhost:8080/} ,就可以看到Resin的主页面,表示Resin已经成功运行,如图:
2 Web应用:论坛
在下面这两章中,我们将会经历一个论坛项目的开发和维护过程,从最初的非常简单的需求,到开发、运行,然后再经历一系列的需求变更和功能改进,来看看使用JDO是如何达到软件的快速开发与维护和应用的随需应变的。并且,还会涉及到一些性能优化和传统JDBC开发很难实现的老问题(如字符串字段长度限制等)。
2.1 准备工作
在开始开发之前,我们必须先弄清楚一些基本的概念,并按J2EE对Web应用的要求建立相应的运行环境。
2.1.1 认识JDO的开发过程 关于JDO的开发流程,以前的文章和网上一些资料也详细介绍过,这里简单地重复一下:
先编写代表数据对象的类,编译生成.class文件,再编写描述该类中每个属性的保存细节的元数据:.jdo文件,然后根据.jdo文件对.class文件进行增强(Enhance),这个数据类就可以在代码中通过JDO的API进行调用了。图示如下:
2.1.2 建立符合J2EE中WebApp规范的应用目录结构
为了减少与Resin的整合配置,我们将论坛应用直接放到<resin>/webapps/下,建立一个名为bbs的子目录。这样,就可以通过在服务器地址后面加入“/bbs/”来访问这个WEB应用了。
在bbs目录中,我们按J2EE规范建立一个“WEB-INF”子目录,然后在WEB-INF中再建立“classes”和“lib”两个子目录,将MySQL的JDBC驱动mysql.jar和JDOGenie的支持包<jdogenie>/lib/目录下的jdogenie.jar和jta.jar两个文件复制到这个<resin>/webapps/bbs/WEB-INF/lib目录中,作为本应用依赖的第三方组件,并将<jdogenie>/license/jdogenie.license文件复制到<resin>/webapps/bbs/WEB-INF/classes/目录下。这些配置就是将jdogenie整合到bbs应用中。
接下来,我们开始确定bbs应用的Java类的包结构。简单起见,我们将源文件都放到“jdobbs”包中,也就是说,这个应用中涉及的Java源代码都会以“package jdobbs;”开头。
这些目录都建好以后,我们得到下面的目录结构:
我们先在bbs目录下放一个index.jsp文件:
<title>JDO BBS on ${pageContext.request.serverName}</title>
您现在的位置:<b>JDO BBS 首页</b>
<h3>欢迎访问JDOBBS!</h3>
<p>本系统采用JDO + JSP 2.0 完成<p>
<p>您的浏览器类型是:${header['user-agent']}
<p>您的IP地址是:${pageContext.request.remoteAddr}
然后将Resin启动,看看效果:
好!现在我们的第一个JSP页面就已经完成了。该页面代码中用到的“${pageContext.request.serverName} ”、“${header['user-agent']}”和“${pageContext.request.remoteAddr}”都是JSP2.0中定义的JSP EL表达式。
2.1.3 一个处理JDO资源的工具类:jdobbs.Sys
我们先来写一个非常关键的工具类:jdobbs.Sys,这个类负责处理一些基本的JDO 存储管理器获取、资源释放、线程同步方面的事务。关于其中的每个细节,都可以用一大篇文章来说明(详情见{http://www.jdocentral.com/forums/index.php?s=8bf08748ea2b95fe52e0d25c7a489732&act=SF&f=11}中的相关讨论),这里为了简单起见,只简单地描述一下这个工具类的目标: 1. 保证每个JSP页面请求的服务端线程中所有的业务逻辑方法共享同一个javax.jdo.PersistenceManager,除非在处理过程中间显式地关闭当前的PersistenceManager。这将采用java.lang.ThreadLocal接口实现
2. 保证每个页面请求结束后,处理请求过程中涉及的JDO资源全部被释放(即关闭用到的PersistenceManager)。这将采用javax.servlet.Filter接口实现
我们来看看下面的Sys.java源码:
package jdobbs;
import javax.jdo.*;
import javax.servlet.*;
import java.io.*;
import java.util.*;
public class Sys extends ThreadLocal implements Filter {
/** 获取PersistenceManagerFactory */
public static PersistenceManagerFactory pmf() {
if(pmf == null) {
Properties p = new Properties();
InputStream is = Sys.class.getResourceAsStream("/jdobbs.jdogenie");
try {
p.load(is);
} catch(IOException ex) {
throw new RuntimeException("初始化PersistenceManagerFactory时配置文件读取失败!",ex);
}
pmf = JDOHelper.getPersistenceManagerFactory(p);
}
return pmf;
}
/** 获取PersistenceManager
* 通过使用ThreadLocal对象,使同一线程中每次调用本方法,都会返回同一PM对象。
*/
public static PersistenceManager pm() {
if(threadLocal == null) {
threadLocal = new Sys();
}
return (PersistenceManager)threadLocal.get();
}
/** 用于释放本次线程中使用到的JDO资源
* @return 是否有未完成的Transaction,便于后台日志区别记录
*/
public static boolean cleanupResource() {
return threadLocal != null && threadLocal.cleanup();
}
private static PersistenceManagerFactory pmf;
//============= 以下是ThreadLocal的相关方法==============
private static Sys threadLocal; //一个用于保证同一线程只访问同一PM对象的控制器
/**
* 获取一个与当前线程相关的PersistenceManager
* @return 一个PersistenceManager类型的对象,调用者需要强制转换一下类型。
*/
public Object get() {
PersistenceManager pm = (PersistenceManager)super.get();
if(pm == null || pm.isClosed()) {
pm = pmf().getPersistenceManager();
set(pm);
}
return pm;
}
/**
* 释放所有与本线程相关的JDO资源
* @return 是否有未完成的Transaction,便于后台日志区别记录
*/
public boolean cleanup(){
PersistenceManager pm = (PersistenceManager)super.get();
if(pm == null) return false;
if(pm.isClosed()) {
set(null); return false;
}
boolean tsActive = false;
try {
Transaction ts = pm.currentTransaction();
tsActive = ts.isActive();
if(tsActive) ts.rollback();
} finally {
set(null);
pm.close();
}
return tsActive;
}
//============= 以上是ThreadLocal的相关方法==============
//============= 以下是JSP Filter的相关方法==============
public void init(FilterConfig p0) throws ServletException {}
public void destroy() {}
/** JSP页面(Servlet)处理的过滤器方法,用于JSP执行完毕后检测并释放用到的JDO资源 */
public void doFilter(ServletRequest req, ServletResponse res, FilterChain
fc) throws ServletException,IOException {
try {
fc.doFilter(req,res);
} finally {
cleanupResource();
}
}
//============= 以上是JSP Filter的相关方法==============
}
简单地说,这个类提供了一个静态的pm()方法,可以获取一个JDO的存储管理器PersistenceManager,这个方法将是以后的代码中用到最多的方法。
其中的Sys. pmf()用于获取PersistenceManagerFactory,这个方法中用到了一个配置文件:jdobbs.jdogenie,这个文件是我们下面将要生成的JDOGenie的配置文件,也就是将在workbench中产生应用配置文件,其中包括数据库连接地址、用户、密码信息,以及一些性能调节的设置。这个配置文件jdobbs.jdogenie将被放置在bbs/WEB-INF/classes/目录下,以便程序读取。
这个Sys类使用了Servlet的过滤接口来保证每个JSP执行完毕后相关的JDO资源得以释放。因此,编译这个类时,一定要将Servlet2.4的支持包包含在CLASSPATH中,这个包即是“<resin>/lib/j2sdk-24.jar”文件。为了以后编译和进行其它工作的方便,我们编写一个Ant脚本作为执行各种批处理(包括编译)的工具。
2.1.4 配置JSP过滤器
为了让前面的JSP Filter生效,我们需要将其配置到我们的Web应用中去,准确地说,就是创建(或更改)bbs/WEB-INF/web.xml文件,我们更改该文件后,看看它的源代码:web.xml
<web-app>
<filter>
<filter-name>JDO_Resource_Cleaner</filter-name>
<filter-class>jdobbs.Sys</filter-class>
</filter>
<filter-mapping>
<filter-name>JDO_Resource_Cleaner</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
这段内容保证了每个JSP页面请求处理完后,Sys.cleanup()方法将得到执行,以检测并清除用到的JDO资源。
2.1.5 Ant脚本
按Ant的标准,我们编写一个名为“build.xml”的脚本,并且,为了配置上的方便,我们将这个文件放到bbs/WEB-INF/目录下。
我们看看这个Ant脚本的源代码:build.xml
<project name="jdobbs" default="enhance">
<property name="resin" location="D:/resin-3.0.4" />
<path id="cp">
<pathelement path="${resin}/lib/jsdk-24.jar" />
<pathelement path="classes" />
<fileset dir="lib" includes="*.jar" />
</path>
<target name="compile">
<javac srcdir="classes" classpathref="cp" debug="true" />
</target>
<target name="enhance" depends="compile" description="Enhance the compiled classes">
<taskdef resource="jdogenie.tasks" classpathref="cp" />
<jdo-enhance project="${ant.project.name}.jdogenie" outputdir="classes" />
</target>
</project>
将这个文件放到bbs/WEB-INF/目录下后,我们开启一个DOS命令行窗口,并进入该目录,运行一下:“ant compile”,将会看到如下结果:
D:\resin-3.0.4\webapps\bbs\WEB-INF>ant compile
Buildfile: build.xml
compile:
[javac] Compiling 1 source file to D:\resin-3.0.4\webapps\bbs\WEB-INF\classes
BUILD SUCCESSFUL
Total time: 2 seconds
D:\resin-3.0.4\webapps\bbs\WEB-INF>
这个结果表示我们的Ant脚本已经成功运行了。如果你看到的结果是:
'ant' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
那就说明你的Ant没有配置好(比如你开了DOS窗口后才去更改系统的PATH环境变量),这方面的问题参见前面的Ant安装与配置进行解决。
脚本的内容这里不一一解释了,请参考Ant的帮助。这里只说明一下:“enhance”批处理中通过jdogenie.jar中的Ant任务定义,使用了一个“jdo-enhance”任务,这个任务就是用来增强类代码的。
好了,进行了以上这些准备工作,为确信以上的工作都没有出现错误,我们再次从浏览器访问一下首页,结果与最开始的首页完全一样,符合预期。
接下来,我们就可以开始进行真正的JDO分析和设计了。
2.2 功能需求
我们最初的需求很简单,就是让网友可以在这个系统中交流,以发贴、回复的形式,简单地看,可以说这是一个留言本系统。我们要达到以下目标: 1. 网友进入主页时,系统列出目前所有的主题,按时间顺序从近到远排列。
2. 网友可以在主页下方的发贴表单中发表新主题,包括标题和内容
3. 其它网友在主页的贴子列表中点击某个标题可以阅读这个主题的详细内容,包括所有的回贴内容。所有回贴按照时间顺序排列在主题主贴后面
4. 可以在主题详细内容页面尾部的回贴表单中回复这个主题
5. 每个网友发表主题或贴子时,系统需要记录该贴子发表时的客户端IP地址
6. 系统提供一个高级搜索功能,让网友可以根据时间、主题标题、内容或回复内容、IP搜索主题
2.3 分析、数据对象模型与JDO集成
通过对需求的分析,我们发现,系统中需要存储的信息就是网友提交的主题或者回复。主题和回复都需要记录发贴的网友的IP地址,都有文字内容,但二者又稍有不同,主题可以有一个标题,而回复不需要(目的是为了方便回贴),另外,一个回复贴子一定是针对一个主题的。
从面向对象的角度出发,我们将以上的数据包装成为下列的对象模型:
(后面我们会看到这个图是如何弄出来的)
2.3.1 编写数据类源码
我们根据贴子的共性,编写一个基类:Post(贴子),代表所有的主题和回复,具有贴子所具备的几个基本属性。而主题类我们用Topic来表示,它从Post继承;回复类我们用Reply来表示,它有一个属性:topic,表示所回复的主题。主题和回复之间具有一对多关系,体现在Topic.replies和Reply.topic属性上。为了JSP2.0中的JSP EL的访问,我们给每个属性加上getter/setter(很多IDE工具都提供自动添加getter/setter的功能)。
下面看看这三个文件的源代码(注意,都放在WEB-INF/classes/jdobbs/目录下):
贴子类Post.java:
package jdobbs;
import java.util.*;
/**
* 网友所发表的贴子,包括话题和回复
*/
public class Post {
/** 贴子内容 */
String content;
/** 贴子内容的长度 */
int length;
/** 发贴时客户机器的IP地址 */
String ip;
/** 发表本贴的时间 */
Date postTime;
public String getContent() {
return content;
}
public void setContent(String value) {
content = value;
}
public int getLength() {
return length;
}
public void setLength(int value) {
length = value;
}
public String getIp() {
return ip;
}
public void setIp(String value) {
ip = value;
}
public Date getPostTime() {
return postTime;
}
public void setPostTime(Date value) {
postTime = value;
}
}
主题类Topic.java:
package jdobbs;
import java.util.*;
/**
* 话题
*/
public class Topic extends Post {
/** 标题 */
String title;
/** 回复数 */
int replyCount;
/** 所有回复 */
List replies;
/** 最后更新,即最后被回复的时间 */
Date lastUpdate;
public String getTitle() {
return title;
}
public void setTitle(String value) {
title = value;
}
public int getReplyCount() {
return replyCount;
}
public void setReplyCount(int value) {
replyCount = value;
}
public List getReplies() {
return replies;
}
public void setReplies(List value) {
replies = value;
}
public Date getLastUpdate() {
return lastUpdate;
}
public void setLastUpdate(Date value) {
lastUpdate = value;
}
}
回复类Reply.java:
package jdobbs;
/**
* 回复
*/
public class Reply extends Post {
/** 所回复的话题 */
Topic topic;
public Topic getTopic() {
return topic;
}
public void setTopic(Topic value) {
topic = value;
}
}
为了在下面使用JDOGenie工具来配置JDO环境,我们先在build.xml所在目录中运行一下编译任务:ant compile,编译这几个类。
2.3.2 配置JDOGenie,自动创建system.jdo元数据文件、自动建数据库,等等
接下来,我们用JDOGenie来实现这个对象模型。注意,接下来这几步很重要!
我们进入<jdogenie>/目录,运行workbench.bat,开始我们的JDO之旅。在主窗口中,我们通过菜单“FileàNew Project…”新建一个JDOGenie项目(这个项目实际上就是JDOGenie的配置文件),在出现的项目文件对话框中,我们选择<resin>/webapps/bbs/WEB-INF/classes/目录,并在文件名输入栏中输入项目名称(也就是配置文件的名称):“jdobbs.jdogenie”,注意,请输入完整文件名。如图:
点击确定后,我们将看到一个项目配置设置的对话框。在这个配置对话框的左边列出配置项,右边则每项的配置内容。注意,带目录图标的“Project”和“Datastore”也是配置项。我们将在这个对话框中逐项地配置必须的信息:
1. 首先出现的是Project属性设置,即左边的Project项,在此项中,我们将“Non-transactional read”检查框选中,这样可以不用声明事务就直接通过存储管理器(PersistenceManager)访问数据库中的对象。如图:
2. 接下来是Ant的集成配置,目的是便于直接在工作台中调用Ant完成数据类的编译和增强。我们需要输入ant.bat和build.xml所在的路径,以便调用。如图:
当然,我们也可以总是手工地在命令行调用Ant,这只需要将上图中的“Disable Ant”选中即可。
3. CLASSPATH设置。这里需要加入数据类的根路径和MySQL的JDBC包,如图:
4. 指定Metadata文件。按照JDO规范的要求,必须用.jdo结尾的文件(也称元数据或描述符)来描述需要存储的数据类。我们选择使用CLASSPATH根目录下的system.jdo文件来描述本系统中所有需要存储的数据类。先选择配置对话框左边的“JDO Meta Data Files”,出现如下对话框:
点击按钮“New”在WEB-INF/classes/下新建一个system.jdo文件,如图:
点击“保存”按钮,回到配置对话框:
5. 在“Project”部分的剩下几个配置项主要用于性能优化等方面的调节,我们暂时不考虑。现在直接点击“Datastore”项,进入数据库连接信息配置项,如图:
点击“Choose”按钮,选择MySQL类型的数据库,然后在URL栏中输入到本机MySQL数据库的连接:“jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK”,该URL中的GBK设置保证MySQL处理中文时不会乱码。配置好后,界面如下:
现在,我们可以点击下方的“Test”按钮来检测数据库是否能连通(在此之前,请先检查一下MySQL是否已经运行)。如果不能连通,请检查前面的配置是否正确(Classpath配置中是否加入了mysql.jar,URL是否输入正确)
到此为止,“Datastore”部分剩下的几项配置都可暂不理会,直接点击最下方的“OK”按钮关闭配置对话框。这时出现的是一个空的界面,如图:
在这里,简单介绍一下JDOGenie的界面,左边是几个大按钮,代表各个方面的功能(前面已经介绍过),从上到下依次是:类树、类图、类列表、数据库结构、JDO查询、远程控制台。后面会介绍这些功能。
我们现在将前面编写的数据类添加到项目中,这个步骤将生成JDO规范中要求的.jdo元数据文件。在上面的界面的上半部分的类列表中点击右键,在弹出菜单中选择“Add Classes…”,这时系统会列出从CLASSPATH中找到的.class类文件,我们选择其中的三个:Post、Topic、Reply:
确定后,系统提示放入哪个元数据文件中,并且列出了前面创建的“system.jdo”文件。直接点击“OK”即可。我们将看到主窗口变成了下面的样子:
工具条上的第四个按钮“JDO Metadata Compile”变成了红色,表示system.jdo中的信息中有歧义,点一下这个按钮看看详细原因:
原来是系统不知道Topic类中的replies列表(List)的元素类型,在主窗口中选中Topic类,在界面下半部分的类属性列表中,在replies属性行上点击右键,选择“Field Properties…”,进入replies属性设置的对话框,在对话框中,我们需要设置两项:第一项,在“Element Type”中选择jdobbs.Reply类,表示replies列表的元素是Reply对象;第二项,在“Inverse field”中选择“topic”,表示Topic.replies与Reply.topic是一对双向关系,任何一边的改动都会影响到另一边。如图:
设置好后,点击这个对话框右上角的“”按钮关闭它,现在在主窗口中,工具条上的第四个按钮已经变成绿色()了,表示元数据已经没有问题了。我们保存一下项目:“File->Save”,现在就可以增强我们的数据类了。选择菜单“BuildàCompile & Enhance”或者点击工具条上的第五个按钮“”,我们将看到成功编译和增强的信息:
下面我们需要为我们的数据类建立相应的数据表,我们点击一下左边的大按钮“ Database schema operations…”,系统会列出当前的数据类所需要的数据表结构,这里我们只看到一个表,因为我们的三个类相互之间是继承关系,JDOGenie对继承的类用同一个表来表示。我们选择菜单“BuildàRecreate Schema”,在数据库中生成这个表结构(如果是以后改变了类,就不能再重建了,而需要在JDO查询区“Queries”中用ALTER TABLE的SQL语句来解决,否则会丢失数据):
以上的一系列配置过程,就是我们集成JDO的过程,乍一看,好象配置非常复杂,其实前面这一系列过程很快就可以完成,理解了JDO的原理后会觉得很简单。以后,这个JDOGenie的工作台workbench.bat就是我们的工作环境了。我们进一步更改类源码后,只需要在这里点击一下编译按钮“”,就可以将最新的源文件编译并增强了。如果增加了更多的数据类,也只需要在这个工作台中将新的类加入到项目中(即元数据中)即可。
最后,我们回想一下前面提到的数据类图,该图实际上也是在JDOGenie的工作台中生成的。我们点击左边的大按钮“ Class and E/R diagrams”进入类图区,在窗口中点右键,选择“Choose Classes…”,将所有的数据类都加入到图中,就会看到包含Post、Topic、Reply三个类的类图,对位置稍加调整,就可以得到前面的类图了,如图:
好了,以上所介绍的是JDOGenie的工作台图开操作界面的诸多细节,实际上,我们还没有涉及JDO开发中的核心部分:API的使用。不过相信经过以上的介绍,读者对作为一个JDO实现的JDOGenie也有了比较全面的认识了。然而,对于JDO开发来说,不同的产品有不同的配置工具和配置操作,各有特色,比如KodoJDO的数据库是可以配置为在运行时动态生成和动态与数据类模型同步的,这一点深受开发人员喜爱。可喜的是,JDOGenie很快也将加进这一非常有利于快速开发和维护的特性。
在我们开始真正的JDO开发之旅之前,我们先休息一会儿,回顾一下前面提到过的各个层次的服务器,包括数据库、JSP服务器、JDO产品等等。在本文的《开发篇》中,将给读者揭开JDO的神秘面纱,让读者看清楚开发过程中的每一个细节……
本文的版权属于笔者本人,但欢迎转载,前提是注明出处和原作者。另外,欢迎在我的专栏中查看我的另几篇文章,并提出宝贵意见!
对该文的评论 人气:2948
KevinAAA (2004-3-31 17:01:19)
写的真的很不错,值得收藏!
sqcool2000 (2004-3-10 17:28:27)
我按照说明做了,创建表的时候出现一个小问题,提示已经成功,但是就是没有表真正生成。我没有写Sys.java,是不是这里有问题呢
fkpwolf (2003-12-13 15:47:32)
写的如此详细,一个字,好!
sun2bin (2003-12-12 13:14:29)
EJB效率不足,配置、调试麻烦,适合数据重要但时间无所谓的大公司 Hibernate免费且用户多,功能也挺强,不过配置麻烦,开发也有些烦琐,而性能则不知道如何 JDO使用开发简单,维护方便,不过目前状况是各家特点不一,产品差异还较大,标准的JDO1.0功能有限。
jenny_cai (2003-12-8 13:51:59)
高手是怎样练成的,就是这样练成的。希望能解释一下各种ORM的优点和不足。ENTITY EJB,HIBERNATION,JDO,随更好?[img][/img]
相关推荐
总之,JDO2-API-2.0是一个强大且灵活的数据持久化框架,它简化了Java应用与数据库之间的交互,提高了开发效率,并允许开发者专注于业务逻辑而不是底层的数据库操作。通过深入理解和熟练使用JDO,可以构建高效、可...
Spring 2.0 的配置是其核心特性之一,它引入了依赖注入(IOC)和面向切面编程(AOP)的概念,极大地简化了Java企业级应用的开发。然而,随着应用规模的扩大,配置文件的数量和复杂度也随之增加,特别是事务配置。在...
《Spring2.0开发参考手册(中文)》是一份深入探讨Spring框架2.0版本的权威指南,旨在帮助开发者全面理解和应用这一强大的Java企业级应用框架。Spring以其依赖注入、AOP(面向切面编程)和模块化设计,极大地简化了...
- **下载JDOGenie并安装**:获取JDOGenie,一个用于简化JDO开发的工具,按照官方文档进行安装配置。 - **新建JDOGenie项目**:在IDE中创建新的项目,并配置JDOGenie的相关属性。 - **创建元数据文件**:为数据类创建...
Java 数据对象(JDO,Java Data Objects)是一种用于在Java应用程序中访问关系数据库的标准API。JDO 提供了一种透明的持久化机制,允许开发者直接操作对象,而无需关心底层数据库的操作细节。JDO 的核心理念是将Java...
对JDO 1.0/2.0的支持。外部依赖spring-jdbc, JDO API, (spring-web)。
《JDO原理及开发》文档详细介绍了JDO的基础知识和实际应用,对理解JDO的工作原理和开发模式具有极大帮助。通过学习,开发者可以更好地利用JDO来简化数据访问层的开发,提高开发效率,同时保持代码的可维护性和灵活性...
3.8.4. ApplicationContext在WEB应用中的实例化 3.9. 粘合代码和可怕的singleton 3.9.1. 使用Singleton-helper类 4. 资源 4.1. 简介 4.2. Resource 接口 4.3. 内置 Resource 实现 4.3.1. UrlResource 4.3.2. ...
SQL Map 使用简单的 XML 配置文件将 Java Bean 映射成 SQL 语句,对比其他的数据库持续层和 ORM 框架(如 JDO 的实现, Hibernate 等), SQL Map 最大的优点在于它简单易学。要使用 SQL Map,只要熟悉Java Bean, ...
配置 JDO" 涉及到的是将 Google 应用引擎(Google App Engine,GAE)与 Spring 框架以及 Java Data Objects(JDO)进行集成的过程。这篇博文可能是指导开发者如何在 GAE 平台上设置 Spring 和 JDO 的详细教程。在 ...
综上所述,将JDO技术应用于Struts框架不仅可以充分发挥Struts框架在Web应用开发方面的优势,还可以利用JDO技术在数据管理和持久化方面的特点,实现更加高效和灵活的Web应用开发。通过构建一个多层体系结构模型,可以...
4. **Web MVC框架**:Spring Web MVC框架在2.0版本中也有了显著提升,提供了模型-视图-控制器架构,支持自定义视图解析器,以及与各种视图技术(如JSP、FreeMarker、Velocity等)的集成,让Web应用开发更加灵活。...
【JAVA Web应用开发:J2EE课件】涵盖了J2EE平台的核心技术和开发流程,适合初学者和进阶者深入理解。以下是对每个文件主要内容的详细解析: 1. **1. 介绍.ppt** - 这部分内容通常会涵盖J2EE(Java 2 Platform, ...
4. **Web MVC 框架**:Spring 2.0 对 Web 层进行了重大改进,提供了更丰富的视图解析器、拦截器等机制,使得 Web 应用程序的开发更加灵活。 5. **数据访问层**:增加了对 JDBC、Hibernate 和 JDO 等持久化技术的...
jdo2.jar.................
Struts2、Spring、JDO(Java Data Objects)和AJAX(Asynchronous JavaScript and XML)是四个在Web应用开发中非常关键的技术。这篇博客“Struts2,Spring,JDO,AJAX on GAE”可能探讨了如何在Google App Engine (GAE)...
本书介绍了JSP在数据库系统中的运用,并对如何构建高效的电子商务应用系统,开发各种中间...本书可作为ERP应用开发人员和广大程序设计人员的指导书,也可作为高等院校师生教学和自学参考书、各类培训机构的培训用书。
查询语言的改进是JDO2.0规范中的重要环节,本文从较高的层面阐述JDO2.0所提供的一些新功能。由于JDO2.0规范还未进入公开草案状态,目前还没有任何内容敲定下来,一切都还可能面临变化。不过,JDO2.0将会很快进入最后...
Spring框架是一个全面的企业级应用开发框架,核心特性包括依赖注入(Dependency Injection,DI)、面向切面编程(Aspect-Oriented Programming,AOP)以及支持多种数据访问技术,如JDBC、Hibernate和JDO。Spring 2.0...
首先,Java是一种广泛使用的面向对象的编程语言,尤其在企业级应用开发中占据主导地位。Java的强大之处在于其跨平台能力,以及丰富的类库和框架支持。 BlazeDS是Adobe公司推出的一个开源项目,它提供了一个灵活的...