`

JDO+JSP2.0开发Web应用向导 – 开发篇

阅读更多
  在前面读者看到的本文的《配置篇》中,我们介绍了用于本文中的基于JDO的WebApp开发的各种环境配置,尤其是本文选用的JDO产品:JDOGenie。JDOGenie的特点就是图形配置工具功能强大,方便使用,学习JDO非常容易。

  我们前面已经完成了一个基本的数据对象模型,并且,每个数据类的属性都用getter/setter进行了封装。这里重温一下这个数据模型:


  这个类图是非常重要的,相当于传统JDBC开发的数据库结构图,但又比数据库结构图更高级一层。实际上,这个类图很容易使用UML工具生成,包括所有数据类的Java源代码。后面的真正的业务逻辑开发过程才是我们的重点,但这个类图却是贯穿整个开发过程的核心,数据库管理员可以根据这个类图来配置并生成数据库结构;开发人员按照这个类图进行对象获取和访问;美工人员可以根据这个类图来使用流行的可视化页面设计工具美化JSP页面(比如显示某个回复的主题标题,就按图中的结构使用${a_reply.topic.title})。

  现在我们已经看到这个类图的重要性,并且也知道这个类图可以通过UML工具快速生成。因此,我们在开发之前几乎不需要任何手工编码,工作的重点就是与根据需求与相关开发人员讨论并确定这个类图结构。这里也体现了JDO开发的一个优点,就是开发之前不需要太多地在意具体的代码,而只需要在面向对象的层面上讨论确立类图的结构即可。讨论类图比讨论数据库结构要容易得多,一方面容易记忆,另一方面一般来说,一个类图会比相应的数据库结构图简单得多(比如一些双向的关系直接在类图上体现,不用象数据库一样需要一个额外的关联表来对应)。


  接下来,我们在《配置篇》的基础上继续进行JDO版论坛的开发。请先快速地在脑海中回顾一下我们使用JDOGenie的工作台生成本应用的项目配置文件,并将数据类导入的过程,下面介绍与前面那些图形界面的操作等价的配置文件。对图形界面不感兴趣的读者也可以直接从这里开始阅读。

1 等价的配置文件代码
  实际上,在《配置篇》中的一切配置步骤,就是为了生成两个文件:一个是描述所有数据类的元数据文件:WEB-INF/classes/system.jdo,其源码如下:

<?xml version="1.0" encoding="UTF-8"?>

<jdo>

<package name="jdobbs">

<class name="Post" />

<class name="Reply" persistence-capable-superclass="Post" />

<class name="Topic" persistence-capable-superclass="Post">

<field name="replies">

<collection element-type="Reply">

<extension vendor-name="jdogenie" key="inverse" value="topic" />

</collection>

</field>

</class>

</package>

</jdo>


  另一个生成的文件是JDOGenie专用的配置文件,包含数据库连接、JDOGenie缓冲配置等等信息。源码如下:


# Server name (must be unique on a machine)
server=jdogenie1
remote.access=true
remote.pm=false
hyperdrive=true

# JDO Genie version
version=2.2.0beta7 (28 Nov 2003)

# Data stores
storeCount=1
store0.name=main
store0.type=jdbc
store0.driver=com.mysql.jdbc.Driver
store0.url=jdbc\:mysql\://localhost/test?useUnicode\=true&characterEncoding\=GBK
store0.db=mysql
store0.user=
store0.password=
store0.properties=
store0.maxActive=5
store0.maxIdle=3
store0.minIdle=2
store0.init.sql=
store0.validate.sql=
store0.retry.interval.ms=1000
store0.retry.count=10
store0.pscache.max=
store0.ext.jdbc-key-generator=-
store0.jdbc.namegen=-

# .jdo resources
jdoFileCount=1
jdo0=system.jdo

# Properties for JDOHelper.getPersistenceManagerFactory(...)
javax.jdo.PersistenceManagerFactoryClass=za.co.hemtech.jdo.client.BootstrapPMF
javax.jdo.option.Optimistic=true
javax.jdo.option.RetainValues=false
javax.jdo.option.RestoreValues=false
javax.jdo.option.IgnoreCache=false
javax.jdo.option.NontransactionalRead=true
javax.jdo.option.NontransactionalWrite=false
javax.jdo.option.Multithreaded=false

# Event logging
event.logging=-
log.downloader=-

# Cache settings
cache.enabled=true
cache.maxobjects=10000
cache.listener=-
query.cache.enabled=true
query.cache.max.queries=10000

# Data store 0 mappings
store0.jdbc.type.count=0
store0.jdbc.javatype.count=0

# Workbench properties (not used at runtime)

mdedit.classPathCount=2
mdedit.cp0=
mdedit.cp1=../lib/mysql.jar

# Workbench Ant configuration (not used at runtime)
ant.disabled=false
ant.buildfile=../build.xml
ant.compile=enhance
ant.show.all.targets=false

# Workbench Scripts (not used at runtime)

# JDO Genie Class Diagrams (not used at runtime)
diagram.count=1

diagram0.name=Diagram 1
diagram0.legend=299,24
diagram0.general=13,Y,Y
diagram0.class=Y,N,Y,N,N,Y,N,Y,N,N,N,N,N,N,N,N,N
diagram0.field=Y,Y,N,N,N,N
diagram0.class.count=3
diagram0.class0=jdobbs.Post,127,48
diagram0.class1=jdobbs.Reply,213,222
diagram0.class2=jdobbs.Topic,33,206


  实际上,我们如果直接手工编写这两个文件的话,也可以不必进行前面那些配置工作。当然,估计很少有人会喜欢手工编码多过使用图形界面。

2 初步测试JDOGenie
  至此我们已经有了数据库结构、有了增强过的数据类,已经可以正式开始使用JDO了。为了加强感性认识,我们先看看JDOGenie的运行情况:我们点击工作台左边的按钮“Grid”进入数据类列表区,选中某个类,点击上面工具条的第11个按钮“View all instances of the selected class…”或者直接按Ctrl+E,系统就会列出该类在数据库中的所有实例对象。不过我们这里还没有任何数据,所以什么也没列出来,以后可以通过这个功能查看某个类的所有实例。如果需要更细致的查询,可以进入左边四个按钮进入JDO查询区,使用JDOQL或者SQL来查询数据。JDOQL的查询界面如图:


  好了,关于JDOGenie的细节已经讲完了。之所以用这么大的篇幅介绍JDOGenie的配置,目的是让读者能够最快速地了解JDO需要的配置信息,并能最快地看到JDO产品的运行。下面我们来真正地开始实现我们的论坛功能。

3 业务逻辑的实现
  为了简单、直观地显示基于JDO的业务开发流程,我们在接下来的功能实现中采用JSP直接访问JDO API来实现数据对象的获取和操纵,就不再累赘地使用JavaBean对话控制器了。

  为了使每个页面的代码更简单,并且又具备调用JDO API、使用常用的Java包、使用JSTL的功能,我们写一个专门的头页面,用于其它页面包含,这样,可以使用其它页面的代码更简洁易读。

<%@page

contentType="text/html; CHARSET=utf8"

import="jdobbs.*,java.util.*,javax.jdo.*,java.io.*"

%>

<%@taglib prefix="c" uri="http://java.sun.com/jstl/core" %>

<%

//预置几个日期格式化工具

pageContext.setAttribute("fullTime",new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

pageContext.setAttribute("shortTime",new java.text.SimpleDateFormat("MM-dd HH:mm"));

pageContext.setAttribute("fullDate",new java.text.SimpleDateFormat("yyyy-MM-dd"));

pageContext.setAttribute("shortDate",new java.text.SimpleDateFormat("MM-dd"));

%>

  这里我们预置了几个用于显示日期的格式化对象,可以方便后面日期数据的显示。在Resin3中,可以用${shortTime.format(someDate)}来以显示类似“09-10 15:33”样式的日期字符串。也许在JSP2.0最终出台之前,这种方式未必规范,但既然服务器支持,就先这么用吧。

  回顾一下论坛的功能需求,主要有以下几点:

1. 网友进入主页时,系统列出目前所有的主题,按时间顺序从近到远排列。

2. 网友可以在主页下方的发贴表单中发表新主题,包括标题和内容

3. 其它网友在主页的贴子列表中点击某个标题可以阅读这个主题的详细内容,包括所有的回贴内容。所有回贴按照时间顺序排列在主题贴后面 4. 可以在主题详细内容页面尾部的回贴表单中回复这个主题

5. 每个网友发表主题或贴子时,系统需要记录该贴子发表时的客户端IP地址

6. 系统提供一个高级搜索功能,让网友可以根据时间、主题标题、内容或回复内容、IP搜索主题


  我们接下来一个一个地实现这些功能。

3.1 主页:列出所有主题
  我们在index.jsp中执行一个不带条件的JDO查询,并按时间倒序用HTML中的表格来显示一条一条的主题。我们将index.jsp的代码改动如下:

<%@page contentType="text/html; CHARSET=utf8" %>

<%@include file="/inc.jsp" %>

<%

//先取得主题列表:

Query q = Sys.pm().newQuery(Topic.class,"");

q.setOrdering("postTime descending");


pageContext.setAttribute("topics",q.execute());

%>

<title>JDO BBS on ${pageContext.request.serverName}</title>

您现在的位置:<b>JDO BBS 首页</b>


<h3>欢迎访问JDOBBS!</h3>


<table border=1>

<tr><th>标题</th><th>内容长度</th><th>发表时间</th><th>IP地址</th><th>回复数</th><th>最后回复</th></tr>

<c:forEach var="topic" items="${topics}">

<tr>

<td><a href="topic.jsp?id=${topic.jdoGetObjectId()}">${topic.title}</a></td>

<td>${topic.length}</td>

<td>${shortTime.format(topic.postTime)}</td>

<td>${topic.ip}</td>

<td>${topic.replyCount}</td>

<td>${shortTime.format(topic.lastUpdate)}</td>

</tr>

</c:forEach>

</table>

  在这段代码中,我们先是使用了一人JDO的javax.jdo.Query来执行查询,对这个Query设置了一个排序参数:“postTime descending”,这个设置使JDO生成“… order by POST_TIME”的SQL子句,完成排序。在JDO中,也可以使用多个排序字段,用“,”号隔开即可。值得一提的是,JDO的排序字段可以是通过引用到达的另一个对象的属性,比如查询Reply对象时,可以按其所回复的主题的回复数进行排序,代码是:

Query query = pm.newQuery(Reply.class,””);

query.setOrdering(“topic.replyCount ascending”); //注意正序也必须写上“ascending”

这两行简单易懂的代码将产生类似

select …

FROM post a LEFT JOIN post AS b ON (a.topic = b.post_id)

WHERE a.jdo_class = 54451616

ORDER BY b.reply_count

  的SQL语句。如果数据库不是MySQL而是Oracle或其它数据库的话,这条SQL语句还会有不同的形式。从中也可以看出JDO的透明性,你只需要按最直接的想法设置排序(“topic.replyCount ascending”),而不用去考虑联表的SQL如何编写,更不用考虑不同的数据库的语法细节。


  由于数据库中还没有数据,我们这里就不给出显示效果,留待后面再说。需要说明的一点是:使用JSP2.0编写的这个index.jsp页面很容易在一般的页面编辑器中进行维护。比如这个页面在DreamweaverMX中编辑时的界面是:


  也许熟悉DreamWeaver的读者会觉得这个表格会将页面撑得比较宽影响美工设计,比如只需要显示顶多3位数字的“回复数”字段在编辑页面时得写成“${topic.replyCount}”,从而将这一单元格撑得过宽,实际上,这样的问题可以通过一个小技巧解决:将该处先写成一个随意的数,再将这个数包(Wrap)上层JSTL表达式“c:out”即可,相应的HTML源码是:<td><c:out value=”${topic.replyCount}”>123</c:out></td>。关于这方面的技巧还有一些,都属于JSP2.0页面美工的内容,这里不再一一描述。

  现在我们继续完成下面的“发表新主题”功能来产生数据。

3.2 发表新主题
  为了新发表贴子,我们需要一个输入表单,现在将这个表单做到首页主题列表的后面,并让这个表单提交到一个名为index!post.jsp的页面,这个接收页面解析提交的参数,并使用JDO API生成新的主题贴子,保存到数据库中。

  先在index.jsp尾部增加一个表单:

<form name=fmPost method=post action="index!post.jsp">

请在这里发表新主题。

<br>标题:<br><input name=title maxlength=100 size=100>

<br>内容:<br><textarea name=content cols=100 rows=10></textarea>

<br><input type=submit value=提交>

</form>

  然后编写接收发贴表单提交的index!post.jsp:

<%@page contentType="text/html; CHARSET=utf8" %>

<%@include file="/inc.jsp" %>

<%

Topic topic = new Topic();

topic.setTitle(request.getParameter("title"));

topic.setContent(request.getParameter("content"));

topic.setLength(topic.getContent().length());

topic.setPostTime(new Date());

topic.setLastUpdate(new Date());

topic.setIp(request.getRemoteAddr());


Sys.pm().currentTransaction().begin();

Sys.pm().makePersistent(topic);

Sys.pm().currentTransaction().commit();


pageContext.setAttribute("topic",topic);

%>


您的主题已经发表,请选择下列操作之一:

<p><a href="index.jsp">返回主题列表</a>

<p><a href="topic.jsp?id=${topic.jdoGetObjectId()}">进入该主题页面</a>

  我们发贴的过程先是生成一个Topic对象,然后使用JDO的API开始一个事务,标记这个新生成的topic对象为存储实例,然后提交事务,这样就完成了对象的保存过程。当事务提交后,这个topic对象就具备了由JDOGenie生成的一个数据库标识,所以我们可以在页面尾部的“进入该主题页面”链接中使用其标识:${topic.jdoGetObjectId()}


  现在我们就可以发表主题了。发表了一系列主题后,我们的论坛主页就比较充实了:
[img]http://lcspace.nease.net/c-j2ee/new-9.files/CSDN_Dev_Image_2003-12-62248498.jpg[img]
3.3 阅读主题及回复
  前面的主题列表页面中的每个主题已经有链接到topic.jsp页面,现在我们来编写这个页面:

<%@page contentType="text/html; CHARSET=utf8" %>

<%@include file="/inc.jsp" %>

<%

//先取得主题:

String oid = request.getParameter("id");

Object objectId = Sys.pm().newObjectIdInstance(Topic.class,oid);

Topic topic = (Topic)Sys.pm().getObjectById(objectId,false);

pageContext.setAttribute("topic",topic);

%>

<title>主题:${topic.title}</title>

您现在的位置:<a href=".">JDO BBS 首页</a> --> <b>主题:${topic.title} </b>

<p>内容:<pre><b>${topic.content}</b></pre>


下面是回复列表:

<table border=1>

<tr><th>回复时间</th><th>IP地址</th><th>内容长度</th><th>回复内容</th></tr>

<c:forEach var="reply" items="${topic.replies}">

<tr> <a name="#${reply.jdoGetObjectId()}">

<td>${shortTime.format(reply.postTime)}</td>

<td>${reply.ip}</td>

<td>${reply.length}</td>

<td><pre>${reply.content}</td>

</tr>

</c:forEach>

</table>

  我们在页面开始先通过访问本页面时必须给出的“id”参数得到一个Topic对象,并将其设置到pageContext中,剩下的代码就是利用JSP2.0显示其内容,包括所有的回复。我们先不给出显示效果,而是接着实现回复主题功能:

3.4 回复主题
  先在刚才的主题页面尾部加上回复表单:

<form name=fmReply method=post action="topic!reply.jsp">

请在这里回复本主题。

<input type=hidden name=topicId value="${topic.jdoGetObjectId()}">

<br>内容:<br><textarea name=content cols=100 rows=10></textarea>

<br><input type=submit value=回复>

</form>

  然后,编写一个接收回复提交的页面:topic!reply.jsp:

<%@page contentType="text/html; CHARSET=utf8" %>

<%@include file="/inc.jsp" %>

<%

Sys.pm().currentTransaction().begin();


//先取得被回复的主题:

String oid = request.getParameter("topicId"); //回复表单中引用的主题贴标识

Object objectId = Sys.pm().newObjectIdInstance(Topic.class,oid);

Topic topic = (Topic)Sys.pm().getObjectById(objectId,false);


//增加回复:

Reply reply = new Reply();

reply.setContent(request.getParameter("content"));

reply.setLength(reply.getContent().length());

reply.setPostTime(new Date());

reply.setIp(request.getRemoteAddr());


topic.getReplies().add(reply); //将新回复加到主题的回复列表中

topic.setReplyCount(topic.getReplyCount()+1); //回复计数加1

topic.setLastUpdate(new Date());


Sys.pm().currentTransaction().commit();


pageContext.setAttribute("topic",topic);

%>


您的回复已经发表,请选择下列操作之一:

<p><a href="index.jsp">返回主题列表</a>

<p><a href="topic.jsp?id=${param.topicId}">进入所回复的主题页面</a>

  在这个页面的开始部分,我们先通过回复表单传递过来的被回复的主题标识取得该主题,然后新生成一个Reply对象,设置好其属性后,将这个新生成的Reply对象添加到Topic的replies列表中。我们这段代码中并没有使用pm.makePersistent()方法,因为将reply对象加到topic对象的replies列表中时,JDO已经标记该reply对象需要保存,这个特性就是JDO中的“可达性存储”概念。如果一个新生成的对象被一个已经存在于数据库中的对象引用,那么,提交事务时这个新对象将被保存,甚至如果这个新对象又引用了另一个新生成的对象,另一个新对象也会被保存。这个特性有时候可以减少我们的代码复杂性。

  细心的读者可能注意到了,最后一行代码中使用了“${param.topicId}”来直接将提交参数中的主题贴标识拼装到返回主题页面的链接中。这使用到了JSP2.0的隐含环境变量param。


  我们测试回复功能,回复了几个贴子后,主题页面的效果如图所示:


  而回到论坛首页,界面如下图:


3.5 高级搜索功能
  到此,我们已经完成了基本的发贴和回复功能,现在做一个组合条件搜索的功能,以体现JDOQL的特点。

  回顾一下搜索功能需求:“让网友可以根据时间、主题标题、内容或回复内容、IP搜索主题”,我们需要编写一个搜索页面,包含一个搜索条件输入表单,这个表单直接提交到本页面,提交后,表单中将显示所输入的条件,而在表单下方,列出符合条件的主题。

  源码 search.jsp:

<%@page contentType="text/html; CHARSET=utf8" %>

<%@include file="/inc.jsp" %>

<title>主题:${topic.title}</title>

您现在的位置:<a href=".">JDO BBS 首页</a> --> <b>搜索论坛主题</b>


<form name=fmSearch>

请在下面输入搜索条件:


<br>发贴日期:

<select name="postTime">

<option value="">不限</option>

<option value="week">一周内</option>

<option value="month">一月内</option>

<option value="year">一年内</option>

</select>

<script>fmSearch.postTime.value = "${param.postTime}"; </script>


<br>标题包含:

<input name=title value="${param.title}">


<br>内容或回复贴的内容包含:

<input name=content value="${param.content}">


<br>发贴IP:

<input name=ip value="${param.ip}">


<br><input type=submit value=搜索>

</form>


<%

//这里进行JDO查询:

Query q = Sys.pm().newQuery(Topic.class);

String jdoql = "";

String paramNames = "";

List params = new ArrayList();


String postTime = request.getParameter("postTime");

if(postTime != null && !postTime.equals("")) {

jdoql += " && postTime >= _time";

paramNames += ",Date _time";

if(postTime.equals("week")) { //最近一周

params.add(new Date(System.currentTimeMillis()-7l*24*60*60*1000));

} else if(postTime.equals("month")) { //最近一月

params.add(new Date(System.currentTimeMillis()-30l*24*60*60*1000));

} else { //最近一年

params.add(new Date(System.currentTimeMillis()-365l*24*60*60*1000));

}

}


String title = request.getParameter("title");

if(title != null && !title.trim().equals("")) {

jdoql += " && title.startsWith(_title)";

paramNames += ",String _title";

params.add("%"+title);

}


String content = request.getParameter("content");

if(content != null && !content.trim().equals("")) {

//主题内容或者某个回复的内容包含子串:

jdoql += " && (content.startsWith(_content) || replies.contains(aReply) && aReply.content.startsWith(_content))";

paramNames += ",String _content";

params.add("%"+content);

q.declareVariables("Reply aReply");

}


String ip = request.getParameter("ip");

if(ip != null && !ip.trim().equals("")) {

jdoql += " && ip == _ip";

paramNames += ",String _ip";

params.add(ip.trim());

}


if(jdoql.startsWith(" && ")) jdoql = jdoql.substring(4);

if(paramNames.startsWith(",")) paramNames = paramNames.substring(1);


q.setFilter(jdoql);

q.declareParameters(paramNames);


pageContext.setAttribute("topics",q.executeWithArray(params.toArray()));

%>


下面是符合条件的主题列表: &&<a href="search.jsp">搜索主题</a>&&<input type=button value="发表新主题" onclick="fmPost.title.focus()">

<table border=1>

<tr><th>标题</th><th>内容长度</th><th>发表时间</th><th>IP地址</th><th>回复数</th><th>最后回复</th></tr>

<c:forEach var="topic" items="${topics}">

<tr>

<td><a href="topic.jsp?id=${topic.jdoGetObjectId()}">${topic.title}</a></td>

<td>${topic.length}</td>

<td>${shortTime.format(topic.postTime)}</td>

<td>${topic.ip}</td>

<td>${topic.replyCount}</td>

<td>${shortTime.format(topic.lastUpdate)}</td>

</tr>

</c:forEach>

</table>

  这个页面执行了一个根据提交的搜索条件拼装的JDOQL,完成了搜索功能。界面如下:


4 功能扩展
  本章中将讨论一些对这个论坛功能进行增强或者解决一些传统JDBC难以解决的问题。

4.1 字符串长度限制
  可能大家有过这样的经验:在一个论坛上原创了一篇很长的文章,结果提交时服务器却提示数据库字段长度不够,只能拆成几贴来发表。这种情况并不少见,有点影响发贴者的积极性。于是,我们希望能够做到:对这种长文本类型的输入信息不作长度限制,想输多少就输多少。即使要限制长度,也是通过其它方式限制,而不是被动地受数据库服务器的限制。

  现在我们可以利用JDOGenie对java.util.List的支持(主流的JDO产品都支持这个JDO可选特性)来技巧性地实现无限制的字符串。方法是将Post.content属性的声明改为List,并在system.jdo中设置该List的元素类型为java.lang.String(通过JDOGenie工作台进行设置),并在Post.content的getter/setter方法中进行一些处理(方法的声明不需要改变,调用的JSP也不需要改变)。具体方法请参考JDOCentral上的笔者共享出来的一个JDO长字符串处理工具类:{http://www.jdocentral.com/forums/index.php?act=ST&f=11&t=564&s=56744171186d115a997fdefbdb46d4a5} 。

  原理清楚后,我们将这段工具代码放到Sys类中,在Sys.java中加入两个新的函数:

/**

* 将长字符串分割存放在一个列表中

* @param value 需要设置的字符串

* @return List类型的字符串片断列表,请将之设到JDO对象的相关属性中。

*/

public static List setLongString(String value) {

if(value == null) return null;


int len = value.length();

int count = (len+partSize-1)/partSize;

List list = new ArrayList(count);


for(int i = 0; i < count; i++) {

int from = i*partSize;

list.add(value.substring(from,Math.min(from+partSize,len)));

}


return list;

}


/**

* 从片断列表中取得长字符串

* @param list 片断列表

* @return 拼装后的长字符串

*/

public static String getLongString(List list) {

if(list == null) return null;

if(list.size() == 1) return (String)list.get(0); //for better performance


//here some cache may be used for faster speed

StringBuffer sb = new StringBuffer();

for(Iterator itr = list.iterator(); itr.hasNext(); ) sb.append(itr.next());


return sb.toString();


}


private static int partSize = 2000;

  然后,在Post类中进行一下修改:

/** 贴子内容 */

List content;


public String getContent() {

return Sys.getLongString(content);

}


public void setContent(String value) {

content = Sys.setLongString(value);

}


最后,还需要修改一下元数据文件system.jdo(可在JDOGenie工作台中进行):

<?xml version="1.0" encoding="UTF-8"?>

<jdo>

<package name="jdobbs">

<class name="Post">

<field name="content">

<collection element-type="java.lang.String" />

</field>

</class>

<class name="Reply" persistence-capable-superclass="Post" />

<class name="Topic" persistence-capable-superclass="Post">

<field name="replies">

<collection element-type="Reply">

<extension vendor-name="jdogenie" key="inverse" value="topic" />

</collection>

</field>

</class>

</package>

</jdo>

  到此为止,代码就算是修改完成了,现在只剩下数据库结构还没有同步。如果不用保留前面的数据,我们简单地在JDOGenie工作台中重建数据库即可,如果需要保留数据,我们可以看看新的数据结构与旧的有什么差别,进行相应的“ALTER TABLE …”和数据复制“INSERT INTO … ”即可。本例中需要进行的SQL操作是:

create table post_content (

post_id INTEGER not null,

seq INTEGER not null,

val VARCHAR(255),

constraint pk_post_content primary key (post_id, seq)

) TYPE = InnoDB;


insert into post_content

select post_id,1,content from post;


alter table post drop column content;


  还有一点需要注意的是,针对Post.content进行查询的JDOQL需要从

content.startsWith(…)

  改成:

content.contains(s) && s.startsWith(…)

4.2 更多扩展功能
  到现在为止,我们已经用JDO和JSP2.0完成了一个具备基本功能的论坛系统,在此基础上,我们还可以扩展更多的功能,比如:

l 贴子列表的分页处理

2 论坛分板块

3 会员功能

4 个性化

5 版主功能

6 审核
   对发贴内容进行审查,通过才能显示出来

7 积分
  为调动用户积极性,加上积分功能,以及引申而出的排行榜、评价投票等等

8 文件上传
  允许用户上传图片或其它与贴子相关的附件

9 反恶意灌水机制
  隔段时间才能再发贴,或图片数字验证等方式

l0 后台日志
  为记录论坛会员和管理员的各种动作,最好在系统中建立一套日志机制,便于出现问题时查询。这方面,首选Log4j({http://jakarta.apache.org/log4j/} )这个第三方组件来完成日志输出任务。


  不过这些功能已经超出本文的范围,这里不再深入开发,有兴趣的读者可以自己完成。

4.3 Resin数据库连接池的使用
  如果希望将数据库的配置放到应用之外进行,我们可以采用Resin的数据库连接池作为JDO的数据库入口。这里,我们可以采用一个虚拟的JDBC驱动来将Resin的连接池包装一下,象普通数据库一样给JDOGenie提供数据库连接服务。

  具体的方法请参考JDOCentral上的代码共享论坛中的贴子:

{http://www.jdocentral.com/forums/index.php?act=ST&f=11&t=547&s=a8f11e9b4343a987a4dc8ed4988c7607}

4.4 团队开发的建议
  如果读者在阅读完本篇文章后,带领一个团队JDO来真正开发WebApp数据库应用的话,最好采用CVS(客户端建议WinCVS)和BugZilla来控制代码变更、Bug跟踪和需求变化。当然,这些与JDO没有多大的关系,不过都是笔者实践过程中非常有用的经验。

5 JDO1.0局限性与JDO2.0
  目前我们还只能在JDO1.0上开发数据库应用,由于JDO还比较年轻,自然存在一些局限性,下面列出影响较大的一些:

1. 增加额外步骤,配置复杂(相对于直接的JDBC)

2. 对数据模型有一定限制(必须有一个无参构造器,属性访问需要getter和setter)

3. 双向对象关系的处理太欠缺(JDO2.0计划中的自动维护的对象关系将解决这些)

4. JDOQL的API稍显累赘(declare一大堆东西,比ODMG的OQL标准还是不如)

5. 没有数据库统计功能(count(),max(),avg()等等,不过已经在JDO2.0计划中)


  JDO2.0将会有专门针对关系数据库的子规范:JDO/R,其中将使JDO能覆盖大多数常用的数据库功能(基于SQL92标准)。当然,有些复杂的SQL操作还是需要JDBC才能完成的,笔者就处理过一些长达上百行的SQL语句。

  JDO2.0专家组目前已经成立,并且已经召开JDO2.0启动会议,指派了负责规范的各个方面的人员,相信很快就会有一个新的Java规范请求(JSR)正式出现在{www.jcp.org}中。据专家估计,一年半后,JDO2.0的规范将会最终完成,同时,也将出现各个厂家推出的JDO2.0产品。

6 参考资料
  JDO出世后,在Java世界引起很大反响,各种褒贬不一的文章层出不穷,包括JavaPro、IBM开发社区、JavaSkyline等技术网站中都有很多文章发表。O’Reilly、PrenticeHall等出版社也先后出版了几本JDO的书。相信以后还会有更多的资料出现。下面介绍一些最为重要的资源。

6.1 主力网站:{file:///D:/_BB/JavaResearch/www.jdocentral.com}
  这个网站是JDO的核心网站,是推动JDO发展的主力。主流的JDO产品和文章都在这个网站上。Sun公司本身也是这个网章的主要成员之一。

有趣的是,这个网站的经典文章区不光收录了主要媒体上发表过的一些JDO相关文章,还收录了几乎所有的反对JDO的文章,这方面也体现了一点客观、公正的态度。

6.2 主力讨论区:{file:///D:/_BB/JavaResearch/www.jdocentral.com/forums}
  作为JDO主力网站中的论坛,这里是JDO的功能、规范、接口、实例等等方面的热烈讨论的地方,很多JDO规范和细节在这里都有体现。这也是作为局外人(JDO Specification Expert Group之外)对JDO提出各种改进意见的最好的地点。目前规范中的一些API和JDO2.0提出的一些API方案就是来源于这里的讨论。

6.3 中文资源
  前面提到的都主要是英文方面的网站,虽然权威,但对不熟英文的开发人员来说,帮助不是很大,下面介绍一些中文方面的JDO资源。

6.3.1 文章与评论集锦{file:///D:/_BB/JavaResearch/www.CSDN.net}
  在{file:///D:/_BB/JavaResearch/www.csdn.net}上的文档搜索条中,输入“JDO”,并选择“文档标题”作为要搜索的目标,并点击“搜索”,将会列出一堆关于JDO的文章。当然,也有很多笔者的文章在其中,读者也可以查看笔者的专栏:{http://www.csdn.net/develop/author/netauthor/sun2bin/}

6.3.2 Q&A论坛:{http://www.javaresearch.org/forum/forum.jsp?column=308}
  这是一个中文的专门为JDBC和JDO开发设立的论坛板块,有什么问题可以直接去这个论坛提问,或者看看别人都经历过什么样的问题。有很多常见问题都已经有详细的回答。

6.3.3 精华资料下载:{http://www.javaresearch.org/dn.jsp}

  这个下载区中有一些关于JDO的书籍(PDF)、教程、示范代码等等,是学习的好材料,也是笔者当初学习JDO的入门资料。

  本文的版权属于笔者本人,但欢迎转载,前提是注明出处和原作者。另外,欢迎在我的专栏中查看我的另几篇文章,并提出宝贵意见!




对该文的评论 人气:5318 
      JasonCao (2004-2-13 0:16:10) 

建议开发使用MVC模式(比如,应用struts框架),这样就可以专注使用JDO构造MODEL一块的功能了。并且建议不要使用PersistentCapable接口的方法,因为这是JDOHancer自动在字节码上增加的,可以使用JDOHelper的一些方法,比如JDOHelper.getObjectId(pc)等。不过,本文也可以作为基本教程了,但作为实际应用开发,还是要使用MVC的,否则无法控制项目规模。

      Reve (2004-1-29 23:51:06) 

tutorial不错,学习这个tutorial,感觉不错,也准备用JDO的方法来改造自己的一个东西。不过JDOGenie并非免费,感觉可能会受限制其实有点盼望,mysql自己出自己的JDO的实现

      andersonmao (2004-1-18 17:17:33) 

JSP2.0的JSTL(如J2SDKEE1.4中) 不是 <%@taglib prefix="c" uri="http://java.sun.com/jstl/core" %> 是 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> =============================================================== JSP EL 在Tomcat里 ${topic.jdoGetObjectId()} ${shortTime.format(topic.lastUpdate)} 方法不能运行,不是JSP2.0的规范吗?

      andersonmao (2004-1-5 16:29:44) 

支持一下

      sun2bin (2003-12-19 19:08:35) 

最新消息:JDOGenie2.2.0beta9今天发布了,改动数据类后可自动同步数据库结构! http://www.jdogenie.com/download.html

      ycflypig (2003-12-18 22:22:17) 

最近的J2EE中有JSP2.0

      nanye18 (2003-12-18 12:28:07) 

jeffyan77你搞错了!上SUN上看看哪个是新版吧。

      jeffyan77 (2003-12-18 6:15:16) 

JSP的最新版本是1.2,怎么会有JSP 2.0?

      dengzi725 (2003-12-15 11:50:13) 

lihai

      mechiland (2003-12-12 18:48:49) 

作者很用心,不错,捧一下场,辛苦了!
分享到:
评论

相关推荐

    Spring in Action(第二版 中文高清版).part2

    7.4 保护Web应用程序 7.4.1 代理Spring Security的过滤器 7.4.2 处理安全上下文 7.4.3 提示用户登录 7.4.4 处理安全例外 7.4.5 强制Web安全性 7.4.6 确保一个安全的通道 7.5 视图层安全 7.5.1 有条件地渲染...

    Spring in Action(第二版 中文高清版).part1

    7.4 保护Web应用程序 7.4.1 代理Spring Security的过滤器 7.4.2 处理安全上下文 7.4.3 提示用户登录 7.4.4 处理安全例外 7.4.5 强制Web安全性 7.4.6 确保一个安全的通道 7.5 视图层安全 7.5.1 有条件地渲染...

    Spring in Action(第2版)中文版

    7.4保护web应用程序 7.4.1代理springsecurity的过滤器 7.4.2处理安全上下文 7.4.3提示用户登录 7.4.4处理安全例外 7.4.5强制web安全性 7.4.6确保一个安全的通道 7.5视图层安全 7.5.1有条件地渲染内容 7.5.2...

    燃料电池汽车Cruise整车仿真模型(燃料电池电电混动整车仿真模型) 1.基于Cruise与MATLAB Simulink联合仿真完成整个模型搭建,策略为多点恒功率(多点功率跟随)式控制策略,策略模

    燃料电池汽车Cruise整车仿真模型(燃料电池电电混动整车仿真模型)。 1.基于Cruise与MATLAB Simulink联合仿真完成整个模型搭建,策略为多点恒功率(多点功率跟随)式控制策略,策略模型具备燃料电池系统电堆控制,电机驱动,再生制动等功能,实现燃料电池车辆全部工作模式,基于项目开发,策略准确; 2.模型物超所值,Cruise模型与Simulink策略有不懂的随时交流; 注:请确定是否需要再买,这种技术类文件出一概不 ;附赠Cruise与Simulink联合仿真的方法心得体会(大概十几页)。

    并列关系-关系图表-鲜艳红色 -3.pptx

    图表分类ppt

    实际项目中三菱fx5u编写的中型程序,用了st fbd ld 混合编程,程序内容完整,控制十来个轴 ,结构清晰 ,用到了结构体,全局变量 ,适合进阶学习

    实际项目中三菱fx5u编写的中型程序,用了st fbd ld 混合编程,程序内容完整,控制十来个轴 ,结构清晰 ,用到了结构体,全局变量 ,适合进阶学习

    并列关系-关系图表-简约折纸-3.pptx

    图表分类ppt

    甘特图-商业图表-稳重色彩 3.pptx

    图表分类ppt

    西南科技大学仿射密码实验报告

    一、实验目的 1.理解仿射密码的基本原理及加密、解密过程。 2.掌握利用 C 语言实现仿射密码加密与解密的基本方法。 3.通过实例观察仿射密码的加密效果及安全性。 4.通过实现简单的古典密码算法,理解密码学的相关概念,如明文、密文、加密密钥、解密密钥、加密算法、解密算法、流密码与分组密码等。

    彩色扇形层级关系图PPT模板-3.pptx

    彩色扇形层级关系图PPT模板-3

    创意铅笔四项并列PPT模板-1.pptx

    图表分类ppt

    大圆套小圆多项包含PPT图表.pptx

    大圆套小圆多项包含PPT图表

    Matlab 运动脉搏数据 基于小波降噪和VMD分解的滤波研究 不同滤波方法的信噪比对比

    Matlab 运动脉搏数据 基于小波降噪和VMD分解的滤波研究 不同滤波方法的信噪比对比

    极简创意PPT时间轴模板.pptx

    图表分类ppt

    CDN技术在网络内容分发的应用解析:工作原理、关键组件及优势

    内容概要:本文详细介绍了CDN (内容分发网络)的技术背景和具体实施方案,旨在帮助技术人员深入了解这一网络优化工具的工作机制。文章首先解释了CDN是如何通过在不同地理位置设立边缘节点来存储缓存内容的,接着重点描述了三个关键组成部分——边缘节点服务器、中心服务器以及负载均衡器的功能,并讨论了它们协同工作的流程。之后阐述了CDN的主要优势,包括提升网站响应时间和内容分发效率、减小源站服务器的压力和支持大规模并发访问等方面的表现。最后列举了几种典型应用场景,如加速静态网页加载速度、保障视频/音频串流质量,还有支持高效的大文件分发等。 适用人群:互联网开发者、运维人员及其他希望改进自己网站或平台性能的专业人士。 使用场景及目标:针对需要优化网站或移动应用程序加载速度的服务提供商来说,采用CDN可以帮助改善用户体验的同时也能节约带宽资源,进而达到降低运营开支的目的。 其他说明:文中不仅讲解了相关概念和技术细节,还提供了实用案例分析以便于读者更加直观的理解CDN的实际应用价值。此外,在选择是否部署CDN时应当考虑自身业务特点和需求,合理规划投入产出比。

    phpAjaxcookbook英文pdf文字版(phpAjax手册)最新版本

    This PDF, available in English, is designed for advanced PHP Ajax learners. It offers 60 simple yet highly effective recipes for the Ajaxification of Web 2.0 sites. Key topics include: - Learning to develop and deploy iPhone web and native applications. - Optimizing the performance of Ajax applications. - Constructing dynamic websites that achieve faster server responses through the asynchronous call feature of PHP Ajax. - Utilizing Ajax for rapid and efficient data access from the server, ther

    机器学习(预测模型):高等教育数学学习评估数据集

    一个专注于高等教育数学学习评估的宝贵资源,它汇集了9546条详尽的记录,为教育领域的数据分析和机器学习研究提供了丰富的素材。该数据集精心设计,能够支持多种机器学习任务,包括分类、回归和聚类等,有助于深入挖掘学生数学学习过程中的关键信息,为教育决策和教学改进提供有力依据。 数据集涵盖了学生回答数学问题的全方位信息。在学生层面,记录了学生ID和国籍,这有助于从不同地域文化背景分析学生的学习表现差异。问题细节方面,每个问题都有独特的ID标识,难度等级分为基础和高级,其中基础问题占比高达82%,高级问题占18%,这种分布反映了数学学习中不同难度层次的覆盖情况。数学主题丰富多样,线性代数主题占比最大,达60%,基础数学占9%,其他主题占31%,涵盖了数学教育的核心领域。子主题进一步细化,向量空间和线性变换是较为突出的两个子主题,分别占比29%和22%,体现了线性代数在数学学习中的重要地位。 数据的预期更新频率为每年,这保证了数据集能够持续反映数学教育领域的最新动态和学生学习情况的变化,使其始终保持时效性和实用性,为教育研究者和实践者提供持续的支持。

    基于java+springboot+vue+mysql的教学辅助平台设计与实现.docx

    基于java+springboot+vue+mysql的教学辅助平台设计与实现.docx

    西南科技大学密码学RSA实验

    内容概要:本文详细描述了RSA密码算法的实现,涵盖从基本原理的理解到密钥生成、数据加密解密的具体编程实践。在密钥生成环节,采用了随机大素数生成、素性检测(使用Miller-Rabin)、以及计算Euler's totient function和选定适当大小的公开指数e和私人指数d的过程;针对加密过程,引入了基于快速模幂算法的数据编码方式,而解密部分,则借助了中国剩余定理(孙子定理),提高了解密速度。同时对可能存在的误差进行了探讨。最后给出了关于如何改进代码性能的方法,并附带解答了一些相关理论问题。 适合人群:熟悉C语言编程,有兴趣深入研究密码学尤其是公共密钥基础设施的专业人士。 使用场景及目标:本报告适用于高校课程作业或是科研项目中的信息安全方向的研究,帮助学生掌握公钥加密机制背后的数学原理和技术细节,提升实际动手能力和解决实际问题的能力。 其他说明:报告还包括了一些附加的内容,比如如何用RSA构建简单的伪随机数生成器,还有讨论了什么情况下加密会产生固定点的现象。这些都是为了加深理解和巩固所学到的知识点提供的补充材料。

    实用的PPT数据表格模板-4.pptx

    实用的PPT数据表格模板-4

Global site tag (gtag.js) - Google Analytics