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

JDO是什么意思?JDO使用实例 为此,我们需要定义一个JDO持久化类来处理Employee对象的持久化。JDO(Java Data Objects)是一种Java持久化技术,它提供了一种独立于数据存储方式的对象持久化机制,允许Java开发人员将对象保存到关系数据库或其他持久化存储中。当然,JDO还有很多其他功能和配置选项,比如查询语言、级联操作、乐观锁等等,这些可以通过JDO文档进行学习和了解。上面的代码创建了一个持久化管理器,并通过pm.makePersistent(emp)方法将Employee对象保存到数据库中。 阅读详情

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=GBK"

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=GBK" %>

<%@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=GBK" %>

<%@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()}

  现在我们就可以发表主题了。发表了一系列主题后,我们的论坛主页就比较充实了:

3.3 阅读主题及回复

  前面的主题列表页面中的每个主题已经有链接到topic.jsp页面,现在我们来编写这个页面:

<%@page contentType="text/html; charset=GBK" %>

<%@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=GBK" %>

<%@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=GBK" %>

<%@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()));

%>

下面是符合条件的主题列表: &nbsp;&nbsp;<a href="search.jsp">搜索主题</a>&nbsp;&nbsp;<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 主力网站:{www.jdocentral.com}

  这个网站是JDO的核心网站,是推动JDO发展的主力。主流的JDO产品和文章都在这个网站上。Sun公司本身也是这个网章的主要成员之一。

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

6.2 主力讨论区:{www.jdocentral.com/forums}

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

6.3 中文资源

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

6.3.1 文章与评论集锦{www.youkuaiyun.com}

  在{www.youkuaiyun.com}上的文档搜索条中,输入“JDO”,并选择“文档标题”作为要搜索的目标,并点击“搜索”,将会列出一堆关于JDO的文章。当然,也有很多笔者的文章在其中,读者也可以查看笔者的专栏:{http://www.youkuaiyun.com/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的入门资料。

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

相关推荐

ZEMAX优化方法---【Zemax学习】

在光学系统自动设计中,优化设计是光学设计中的重要阶段,也是ZEMAX软件的核心功能,涉及评价函数、权因子、阻尼因子等重要概念。在常用的阻尼最小二乘法优化设计的基础上,ZEMAX给我们提供了三种优化选择:Optimization(局部优化)、Global Search(全局优化)和 Hammer Optimization(锤形优化)。

yh的博客 4449

JDO快速入门

<br />JDO快速入门Java数据对象(Java Data Objects,JDO)是一个应用程序接口(API),它是Java程序员能够间接地访问数据库,也就是说,不需使用直接的结构化查询语言(SQL)语句。JDO是作为Java数据库连接(JDBC)的一个补充来介绍的,而JDBC是一个支持使用SOL语句对流行的数据库程序进行访问的接口。有了 JDO,程序员就可以使用类来定义数据对象,然后支撑程序就会根据类的定义来管理对给定数据库的实际的数据访问了。  <br /><br />      JDO是以Sun

liuzhigang1237的专栏 1万+

别再手动建模了!用Simscape Multibody Link一键导入SolidWorks装配体到Simulink

本文介绍了如何利用Simscape Multibody Link插件将SolidWorks装配体一键导入Simulink,实现CAD到仿真的无缝衔接。该技术显著提升机械系统设计与控制效率,特别适用于工业机器人、车辆悬架等复杂机构的动力学分析。文章详细解析了插件安装、模型导出优化及仿真验证的全流程,帮助工程师摆脱手动建模的低效工作模式。

weixin_27256197的博客 461

JDO 的架构作一个简单的介绍

https://www.2cto.com/kf/201601/484672.html

编程reborn 720

Star CCM+中的物性参数(定值、多项式、查表)

仿真过程中我们一定会遇到设置物性参数的问题,对于固定的物性参数还好说,直接输入一个固定的值即可。但是对于冷却液、气体、两相流等,物性参数是随着温度或温度和压力变化的。此时就涉及到多项式和查表。首先创建物理连续体,选择计算模型。

2201_75375391的博客 2957

GraphRAG + Ollama 本地化部署实战:从零搭建知识图谱问答系统

本文详细介绍了如何在本地部署GraphRAG与Ollama,搭建一个完全自主可控的知识图谱问答系统。通过逐步指导环境配置、项目初始化、核心设置修改以及源码适配,成功将GraphRAG从依赖云端OpenAI API转向本地Ollama服务,并选用qwen2.5:7b和nomic-embed-text模型。该方案有效解决了数据隐私与持续API成本问题,特别适合中小企业构建内部知识库或智能客服系统。

x2y3z4a5b的博客 919

Java数据对象(JDO)快速入门

  JDO有一个充满活力的社区。所以如果你正在为你的POJO寻找持久化解决方案,JDO是一个通过JCP项目开发的标准。JDO为POJO持久化提供了一个丰富和具有全部特征的JSR规范,并且很多开发商正在提供竞争性的JDO实现。EJB3.0,正由Sun微系统和来自于不同的持久化开发商的专家创建,将是未来持久化模型的选择。  持久化Java技术对象到任何关系型数据库都是一项富有挑战性的任务,因为它必须潜

银狐的专栏 3625

4.4 线上vs离线:使用 Langfuse 实现智能体的全方位无死角评估

本文介绍了如何利用Langfuse工具实现AI智能体的全方位评估,包括线上和离线两种评估方式。线上评估通过收集用户实时反馈(如点赞/点踩)来监控真实表现,而离线评估则通过创建标准化数据集进行批量测试和自动评分。文章详细演示了如何为"旅小智"AI应用添加反馈功能、创建评估数据集,并利用Langfuse SDK进行自动化评估和分析。通过这两种评估方式的结合,开发者可以建立数据驱动的AI质量保障体系,实现持续优化和版本迭代。

yonggeit的博客 3312

SUN 的JDO标准

source:http://blog.csdn.net/legendinfo/archive/2004/11/29/197392.aspxJDO允许我们使用Java对象,支持事务和多用户。与ODBC不同的是,它使我们无需考虑SQL和与数据库有关的其他东西。它与串行化也有所区别,因为它支持多个用户和事务。JDO允许Java开发人员将他们的数据模型用作数据模型,无需在“数据端”、“对象端”之间的转移方

§为艺术而技术§ 1167

代码接单:用 ComfyUI 搭建电商模特换装工作流(保姆级教程)

在闲鱼和小红书上,搜一下“AI 模特图”或者“服装换模特”,你会发现大量的接单需求。太贵:请个外模拍一套图,动辄几千块。太慢:寄样品、排期、拍摄、修图,半个月过去了。太卷:竞对都在上新,你还在等图。“给我一张你的衣服平铺图(或假人图),我 10 分钟给你生成真人上身图,一张只要 50 块。对于商家是省钱,对于你是纯利润。为什么不用 Midjourney?因为 MJ 抽卡不可控,无法保持衣服版型一致。只有 ComfyUI,才能做到精准控制,实现工业化量产。

BUG猿的博客 1779

Matlab 轴承信号仿真+分析

文章目录项目说明1、信号仿真1.1 仿真公式1.2 信号参数设置1.3 代码实现2、仿真信号分析2.1 小波变换2.2 快速谱峭度 项目说明 本文主要解决两个问题: 1、仿真包含周期性冲击信号、随机干扰冲击信号、调制干扰信号以及随机噪声四个成分的故障轴承信号; 2、使用小波变换以及快速谱峭度对该仿真信号进行分析。 1、信号仿真 1.1 仿真公式 这里的仿真信号公式主要参考了包文杰学者的硕士论文《加权谱峭度故障诊断方法研究与应用》,列举公式如下: 周期性冲击信号仿真 随机干扰冲击信号仿真 调制

cocofisher的博客 9267

JDO+JSP2.0开发Web应用向导 - 配置篇

JDO+JSP2.0开发Web应用向导 – 配置篇   本文将主要介绍目前流行的JDO规范的应用开发过程。为简单起见,将会使用JSP2.0来进行页面部分的开发,因为其JSP EL可以大大简化页面的开发,节省调试时间,并增加页面的可读性。  以前曾经在优快云上发表过一篇关于用JDO开发数据库应用的文章,那只是基于数据库的简单的文本模式的程序,仅仅是为了表达清楚JDO开发的原理和基本过程

sun2bin的专栏 4351

Qt C++ 串口调试助手

1. **串口配置**:支持自动检测可用串口,可配置波特率(9600~921600)、数据位(5~8)、校验位(无/奇/偶/标记/空格)、停止位(1/1.5/2)。- HEX 模式发送时,输入格式需为十六进制字符(0-9、A-F),可带空格(如 `01 A3 FF`)或不带空格(如 `01A3FF`)。// 发送模式:true=HEX,false=ASCII。// 字符串转HEX字节数组。ui->parity->addItems({"无", "奇校验", "偶校验", "标记校验", "空格校验"});

寒寒豆豆软件公司的博客 147

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

  在前面读者看到的本文的《配置篇》中,我们介绍了用于本文中的基于JDO的WebApp开发的各种环境配置,尤其是本文选用的JDO产品:JDOGenie。JDOGenie的特点就是图形配置工具功能强大,方便使用,学习JDO非常容易。   我们前面已经完成了一个基本的数据对象模型,并且,每个数据类的属性都用getter/setter进行了封装。这里重温一下这个数据模型: [img]http...

sunix0的专栏 224

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

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

<!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--><script type="text/javascript"

zgqtxwd的专栏 468

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

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

  本文将主要介绍目前流行的JDO规范的应用开发过程。为简单起见,将会使用JSP2.0来进行页面部分的开发,因为其JSP EL可以大大简化页面的开发,节省调试时间,并增加页面的可读性。   以前曾经在优快云上发表过一篇关于用JDO开发数据库应用的文章,那只是基于数据库的简单的文本模式的程序,仅仅是为了表达清楚JDO开发的原理和基本过程,现在这篇文章,将更加注重实用性,向各位感兴趣的读者详细...

sunix0的专栏 247

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

风云变幻之JDO2.0

风云变幻之JDO2.0JDO2.0一波三折,尤其这一次,遭受重创。JDO2.0立项以后,道路就不平坦,平静中潜伏杀机,我早已觉得有写一点东西的必要了。从9月份的被迫的EJB合并,到现在的大打出手,可谓惨不忍睹。而投票结果更是多年来JCP中最具"政治"意味的投票,因为没有任何一个反对票是基于技术理由。经过几天的心情沉重之后,我终于可以静下心来反思一下了,为什么会这样,以后会怎样,现在可以

sun2bin的专栏 5059

JDO能给我们带来什么

JDO能给我们带来什么 - 成本,管理,快速开发,随需应变……(本文的版权属作者本人,欢迎转载,但必须注明出处和原作者)JDO规范的出台,给了传统的数据库应用,尤其是WEB方式的数据库应用一个新的机会,就是采用透明的中间件存储技术,减少传统开发的繁琐工作,提高开发和维护的效率,使我们能够应对新的互联网时代的日益迅速的需求变化。从开发的角度来看

sun2bin的专栏 1271

关于java社团应用技术的一次摸底

在51上转了一晚上,收集到一些资料,加以整理,一些生僻的死角又一次呈现收集require skill:job 1.服务端   jsp数据访问 jdbc前端     html, css, javascript,xmlWeb2.0 technique, such as XML, AJAX, RSS, Tagjob 2.基础: 多线程,oo:接口,重用思想web框架:Struts, Spring MVC

mweiguo 1348

JDO能给我们带来什么-成本,管理,快速开发,随需应变……

<!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--><script type="text/javascript"

zgqtxwd的专栏 535

java做jsp问题,java、J2EE基础问题汇总-JSP教程,Java技巧及代码

一、java基础方面:1,作用域public,protected,private,以及不写时的区别public 在其他的包中的类也可以引用,protected只限于同一个包内的类,private只有自己可以使用。不写的时候和protected一样。2,arraylist和vector的区别arraylist需要预先定义大小,vector不用;vector的方法都是同步的(synchronized)...

weixin_35940949的博客 147

java web环境构造_构造Jsp/javabean开发和发布环境的简单办法

更多构造Jsp/javabean开发和发布环境的简单办法板桥里人 htp://www.jdon.com 2002/05/19以Java为基础的J2EE是最新的电子商务解决方案,其复杂性和开发工具系统的昂贵也使不少人却步.在实际项目应用中,真正需要完全使用J2EE方案的并不多,面对中小型企业电子商务应用,下列组合足够对付:Jsp/servlet+ Javabeans(taglib) + MySQL(...

weixin_39535125的博客 159

JqWeb-framework v2.0

JqWeb-framework:* Dual licensed under the MIT and GPL licenses:* http://www.opensource.org/licenses/mit-license.p...

114

Web平台的动态设计技术

ASP和PHP技术则在国内比较流行,但是由于PHP自身的缺点,并不是很适合开发电子商务系统,JSP在国际上比较流行,适合开发一些企业级电子商务系统,但现在 在国内运用的还比较少,在技术上和成本上要求都比较高。因此对于一些中小企业开发B2C型电子商务系统,采用微软的IIS+ASP方式,开发周期短,成本低,通过引入XML技术来描述及传送数据内容,可以开发出灵活的电子商务应用,但是ASP的跨平台能力差,目前只能运行在微软的Web服务器上,且安全性不好,需要注意经常下载微软发布的补丁程序。5、ASP.NET技术。

qq_61141142的博客 404
上一篇: JDO之前世今生
下一篇: JDO+JSP2.0开发Web应用向导 - 配置篇
sun2bin
博客等级 码龄25年 1粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值