2、极端体验:初学者10分钟实现完整的单表增删改查功能
【关闭 】
本站发布时间:2008年05月06日 09:10
概述 :对于数据库应用来说,单表的增删改查功能可以揭示应用框架对业务系统开发的基本支持。7wxAop中实现这一功能非常简单,所有代码都可由系统工具生成,而且代码量很小,易于阅读分析。
下面的例子,后台程序都要访问数据库。在7wxAop中,绝大部分的业务过程(Action)都是通过AutoSQL实现的,AutoSQL类似于iBatis,与iBatis一样用SQL表示持久接口,用SQL中的占位符号表示持久接口的输入参数;不同的是AutoSQL中输入参数充分考虑了Web开发下的实际情况,同时,还在SQL中指定了符合Ajax要求的标准数据输出。一个或多个AutoSQL够成AutoAction,形式上AutoAction类似于存储过程,但输入输出方式都是以Web下的Ajax开发为基础开发,所以用于Ajax应用时,可实现最简洁的代码。
我们现在实验如何在10分钟内,从建表开始,开发一个完整的单数据表应用--图书管理,该应用含图书的增、删、改、查(可翻页、单击表头排序的列表)、表单输入校验等功能。
在7wxAop中,数据库应用程序前后端的基本代码都可以在系统工具DBView 中生成。实际上,无需任何代码,DBView本身就可以实现任意数据库、任意数据表的增删改查功能;但在实际应用中,我们需要生成代码,以便程序员后期定制不同的功能。
注意 :
代码生成器最近更新,早于 07-04-16 19:00 下载xjawa.zip的用户,需下载更新以下文件:
1、建表
在Eclipse中启动MiniServer后,打开后台管理界面(http://localhost/admin/ ),进DBView,左边导航树显示的是系统默认业务数据库WebApp的表和视图,点〈Execute SQL 〉,右边出现SQL执行控制台界面:
在SQL输入框中,粘贴入以下建表代码:
CREATE TABLE book( ID BIGINT NOT NULL, TITLE VARCHAR(40) NOT NULL, AUTHOR VARCHAR(10) , PUBLISHER VARCHAR(20), PUBLISHDATE DATE, CONTENT VARCHAR(1000), PRICE DECIMAL(4,2) NOT NULL, NUMBER INT NOT NULL, PRIMARY KEY(ID) );
按<Run>,执行建表操作。然后按左侧导航界面上的刷新图标 ,在Tables列表中就可以看到新建的BOOK表,点击BOOK,右侧出现该表的元数据定义Definition页及数据Data页,在Data页中,我们可以直接增删改查,但我们现在要产生自己的代码。
2、产生后台代码
在Definition页,按<AutoSQL>按钮可以产生后台程序代码AutoAction及AutoSQL:
按〈Action Servlet Template〉,进入Action Servlet Template对话框,我们可以产生一个7wxAop应用(即WebActions 子类,如HelloWorld教程中的aop.Test类)的代码模板,由于我们打算这个例子与HelloWorld例子共用WebActions子类aop.Test,因此不用复制整个模板,只需把其中的方法defineAutoActions() 复制并插入到aop.Test即可。然后,我们退出Action Servlet Template对话框,从上图界面中复制“Normal SQL”之上的所有行,插入到defineAutoActions() 方法的actions变量赋值语句中,最后形成的aop.Test代码为:
package aop; import org.xjawa.system.WebActions; import javax.servlet.http.HttpServletRequest; public class Test extends WebActions { public String [][] defineAutoActions () throws Exception { String [][] actions = { // 取 BOOK 列表 { "@getBooks" , "" , "books(start,len):=SELECT_NOMETA ID,TITLE,AUTHOR,PUBLISHER,PUBLISHDATE,CONTENT,PRICE,NUMBER" + " FROM BOOK where {_where} order by {_orderby}" , } , // 取一条 BOOK { "@getBook" , "" , "book:=SELECT ID,TITLE,AUTHOR,PUBLISHER,PUBLISHDATE,CONTENT,PRICE,NUMBER" + " FROM BOOK" + " WHERE ID={_id}" , } , // 插入一条 BOOK { "@addBook" , "" , "INSERT INTO BOOK " + "(ID,TITLE,AUTHOR,PUBLISHER,PUBLISHDATE,CONTENT,PRICE,NUMBER) VALUES" + "({fieldmax(SELECT MAX(ID) FROM BOOK)_id},'{_title}','{_author}','{_publisher}','{_publishdate(NULL)}','{_content}',{_price},{_number})" , "id:=requestvalue_id" , } , // 更新一条 BOOK { "@updateBook" , "" , "UPDATE BOOK SET " + "TITLE='{_title}'" + ",AUTHOR='{_author}'" + ",PUBLISHER='{_publisher}'" + ",PUBLISHDATE='{_publishdate(NULL)}'" + ",CONTENT='{_content}'" + ",PRICE={_price}" + ",NUMBER={_number}" + " WHERE ID={_id}" , } , // 删除一条 BOOK { "@delBook" , "" , "DELETE FROM BOOK " + " WHERE ID IN ({array_id})" , } , } ; return actions; } public void _helloServer ( HttpServletRequest request ) throws Exception { sendPrimary ( request, "info" , "Hello, " + request.getParameter ( "myname" ) + "! This's Aop Server :" + request.getServerName ()) ; } }
这就是本例子的唯一的后台代码,含5个AutoAction,没有XML,也没有业务Bean、DAO、实体Bean等等繁琐的东西。
后端代码完成后,停止MiniServer并重新启动。
3、产生前端代码
注意 :
7wxAop框架前后台都基于unicode设计,因此前台代码(html,js,jsp)必须存储为utf-8 文件格式,否则显示可能出现乱码。
列表页生成 :在BOOK表的Definition页,按<ListView>按钮进入ListView生成界面,顶部的代码生成选项我们分别选择:
不排序 点击标题排序 不选择 多选 单选 仅表格模板 完整的列表操作界面 嵌入主表的子表数据列表
当程序提示输入后台“应用程序名”时,输入“aop.Test”。在WWW下的7wx文件夹中创建空的books.html文件,将产生的代码复制到该文件中,books.html文件代码如下(注意以utf-8 格式保存):
< HTML>
< HEAD>
< TITLE> Book List < / TITLE>
< META NAME= "Generator" CONTENT= "7WX/AOP Framework" >
< META http-equiv= "content-type" content= "text/html; charset=utf-8" >
< !-- 导入前端系统库 -->
< SCRIPT src = "/commonjs/7wx.js" > < / SCRIPT>
< / HEAD>
< !-- = = = = = = = = = = = = = = = HTML area = = = = = = = = = = = = = = = = = = = = = = = -->
< BODY leftmargin= 0 topmargin= 0 >
< table class= layout cellpadding= 0 cellspacing= 0 >
< !-- A.功能按钮区 -->
< tr class= toolbar> < td>
< table cellpadding= 0 cellspacing= 0 width = 100 % > < tr>
< td>
< button id= buttonAdd onclick= "addBook();" > 新建< / button >
< button id= buttonMod onclick= "modBook();" > 修改< / button >
< button id= buttonDel onclick= "delBook();" > 删除< / button >
< !-- < button id= buttonSearch onclick= "Xdialog('searchbook.html');" > 查找< / button > -->
< / td>
< td align= right>
< select id= smartSearch onchange= "smartSearchChange();" >
< option value = "1=1" selected > 快速查找< / option>
< !-- < option value = "cloumnA=0" > 1 .某条件< / option> -->
< option value = "findtitle" > 2 .查找标题< / option>
< !-- < option value = "findcolumnA" > 3 .查找某列< / option> -->
< / select >
< / td>
< / tr> < / table>
< / td> < / tr>
< !-- B.业务数据区:列表 -->
< tr> < td> < div class= listviewcontainer>
< !-- 列表模板开始--------------------------------- -->
< TABLE id= booklist width = 100 % class= clistview>
< TR align= center class= clistviewhead>
< TD width = 20 nowrap> < input type = "checkbox" value = "全选" onclick= "XsetAllTableLinesChecked(booklist,this.checked);" > < / TD>
< TD nowrap width = 20 style= 'cursor:hand' onclick= "reOrder('ID')" > ID< / TD>
< TD nowrap width = 130 style= 'cursor:hand' onclick= "reOrder('TITLE')" > TITLE< / TD>
< TD nowrap width = 40 style= 'cursor:hand' onclick= "reOrder('AUTHOR')" > AUTHOR< / TD>
< TD nowrap width = 70 style= 'cursor:hand' onclick= "reOrder('PUBLISHER')" > PUBLISHER< / TD>
< TD nowrap width = 40 style= 'cursor:hand' onclick= "reOrder('PUBLISHDATE')" > PUBLISHDATE< / TD>
< TD nowrap width = 160 style= 'cursor:hand' onclick= "reOrder('CONTENT')" > CONTENT< / TD>
< TD nowrap width = 22 style= 'cursor:hand' onclick= "reOrder('PRICE')" > PRICE< / TD>
< TD nowrap width = 43 style= 'cursor:hand' onclick= "reOrder('NUMBER')" > NUMBER< / TD>
< / TR>
< TR style= 'display:none;' >
< TD> < INPUT type = checkbox XobjectID= { books_ID} XhelpValue= { books_ID} > < / TD>
< TD nowrap align= 'right' style= 'color:blue ;' > { books_ID} < / TD>
< TD nowrap> { books_TITLE} < / TD>
< TD nowrap> { books_AUTHOR} < / TD>
< TD nowrap> { books_PUBLISHER} < / TD>
< TD nowrap> { books_PUBLISHDATE} < / TD>
< TD nowrap> { books_CONTENT} < / TD>
< TD nowrap align= 'right' style= 'color:blue ;' > { books_PRICE} < / TD>
< TD nowrap align= 'right' style= 'color:blue ;' > { books_NUMBER} < / TD>
< / TR>
< / TABLE>
< !-- 列表模板结束--------------------------------- -->
< / div> < / td> < / tr>
< !-- C.业务数据区:列表翻页控制 -->
< tr class= pagebar> < td>
< SCRIPT> XinsertPageControl(15 )< / SCRIPT>
< / td> < / tr>
< / table>
< / BODY>
< !-- = = = = = = = = = = = = = = = Script area = = = = = = = = = = = = = = = = = = = = = = = -->
< SCRIPT LANGUAGE= "JavaScript" >
< !--
////////////////=====A.界面初始化=========
var orderBy = "id" ; //默认排序
var searchWhere = "" ; //查找条件 格式为“ AND (name='test')”
var smartWhere = "1=1" ; //默认的快速查找条件
//界面初始化
function loadok () {
XsetAppAndAction("aop.Test" ,"getBooks" ); //设定应用程序servlet及取列表数据的请求动作名
loadBooks(); //取列表数据
}
//快速查找条件改变
function smartSearchChange () {
if (smartSearch.value = = "findtitle" ){ //需额外参数型快速查找
var title = window .prompt ('模糊查找标题,请输入关键字:' ,'' );
if (title != null)
smartWhere = "title like '%" +title +"%'" ;
//else if(smartSearch.value=="findcolumnA"){ //需额外参数型快速查找
// var title = window.prompt('模糊查找columnA,请输入关键字:','');
// if(title!=null)
// smartWhere = "columnA like '%"+title+"%'";
} else //简单快速查找
smartWhere = smartSearch.value ;
loadBooks();
}
//取数据,初始进入,改变排序,查找、快速查找时调用此函数
function loadBooks () {
XnewQuery("&where=" +smartWhere + searchWhere + "&orderby=" +orderBy,true); //访问后台程序
}
//页数据显示,调用loadRoles()或翻页操作,服务器返回数据时,将调用此函数
function on_getBooks () {
XfillTableWithArray(booklist,buffer.books); //合成列表显示
XresetPageCtl(buffer.books_count); //重置翻页控制条
}
////////////////=====B.用户动作响应=========
//排序列表,在点击表头时调用
function reOrder (colName) {
orderBy = (orderBy= = colName)? (colName+" DESC" ) : colName; //按某列排序,再点反序正序切换
loadBooks();
}
//增加Book
function addBook () {
Xdialog1("book.html?id=-1" ); //调用表单页,id=-1 为新增
}
//更新Book
function modBook () {
var selectedItems = XgetTableSelectedLines(booklist); //取选中行的id集合
if (selectedItems.length = = 0 ){
alert ('进行该操作只前,请先选择book。' );
return ;
}
for (var i= 0 ; i< selectedItems.length ; i++)
Xdialog1("book.html?id=" +selectedItems[i]); //调用表单页,id=有效值 为更新
}
//删除Book
function delBook () {
var url = checkSelectURL(booklist,"id" );
if (url.length > 0 )
if (confirm ("确定要删除这些book吗?" ))
callServer("aop.Test" ,'delBook' ,url);
}
//选择book辅助函数
function checkSelectURL (tab,para) {
var URL = XgetTableSelectedLinesURL(tab,para); //取选中行的id集合构成的url
if (URL .length = = 0 ) alert ('进行该操作之前,请先选择book。' );
return URL ;
}
////////////////=====C.对话框或后台动作响应=========
function onAddBook () { Xrefresh(); } //在表单页book.html用于新建时,新建成功后调用
function onUpdateBook () { Xrefresh(); } //在表单页book.html用于更新时,更新成功后调用
function on_delBook () { Xrefresh(); }
function onSearchBook (where) { //在查询页searchbook.html中调用
searchWhere= where;
loadBooks(); //按新查询条件载入新数据
}
/////////////////======D.显示格式化函数================
setTimeout ('loadok()' ,1 );
//-->
< / SCRIPT>
< / HTML>
表单页生成 :在BOOK表的Definition页,按<FormView>按钮进入FormView生成界面,顶部的代码生成选项我们分别选择:
输入域排成 1 2 3 列 仅表单模板 完整表单界面 完整表单界面含子表输入区
当程序提示输入后台“应用程序名”时,输入“aop.Test”。在WWW下的7wx文件夹中创建空的book.html文件(在7wxAop中,默认表单页文件名为数据表名,列表页文件名为表名的复数形式),将产生的代码复制到该文件中,book.html文件代码如下(注意以utf-8 格式保存):
< HTML>
< HEAD>
< TITLE> Book < / TITLE>
< META NAME= "Generator" CONTENT= "7WX/AOP Framework" >
< META http-equiv= "content-type" content= "text/html; charset=utf-8" >
< !-- 导入前端系统库 -->
< SCRIPT src = "/commonjs/7wx.js" > < / SCRIPT>
< / HEAD>
< !-- = = = = = = = = = = = = = = = HTML area = = = = = = = = = = = = = = = = = = = = = = = -->
< BODY leftmargin= 0 topmargin= 0 >
< table class= layout cellpadding= 0 cellspacing= 0 >
< !-- A.标题区 -->
< tr class= formtitle> < td>
Book属性
< / td> < / tr>
< !-- B.表单区 -->
< tr> < td class= formcontainer>
< !-- 表单模板开始----------------------------- -->
< form id= "bookform" >
< table border = 0 cellpadding= 2 cellspacing= 0 width = 100 % class= formalltable>
< tr>
< td> < input type = 'hidden' name = 'id' > < / td>
< / tr>
< tr>
< td align= right> < span id= 'xcLabel_title' > < / span> :< / td>
< td> < input type = 'text' name = 'title' > < / td>
< / tr>
< tr>
< td align= right> < span id= 'xcLabel_author' > < / span> :< / td>
< td> < input type = 'text' name = 'author' > < / td>
< / tr>
< tr>
< td align= right> < span id= 'xcLabel_publisher' > < / span> :< / td>
< td> < input type = 'text' name = 'publisher' > < / td>
< / tr>
< tr>
< td align= right> < span id= 'xcLabel_publishdate' > < / span> :< / td>
< td> < input type = 'text' name = 'publishdate' readonly> & nbsp; < a onclick= 'XselectDateFor(bookform.publishdate)' style= 'cursor:hand' > 选择< / a> < / td>
< / tr>
< tr>
< td align= right> < span id= 'xcLabel_content' > < / span> :< / td>
< td> < input type = 'text' name = 'content' > < / td>
< / tr>
< tr>
< td align= right> < span id= 'xcLabel_price' > < / span> :< / td>
< td> < input type = 'text' name = 'price' > < / td>
< / tr>
< tr>
< td align= right> < span id= 'xcLabel_number' > < / span> :< / td>
< td> < input type = 'text' name = 'number' > < / td>
< / tr>
< / table>
< / form >
< !-- 表单模板结束----------------------------- -->
< !-- 全部表单区域结束-------------------------- -->
< / td> < / tr>
< !-- C.按钮区 -->
< tr class= buttonbar> < td>
< div id= xeditbar>
< button onclick= "okClick('save');" accesskey= "s" > 保存(S) < / button >
< button id= nextbtn onclick= "okClick('saveAndNext');"
style= "display:none;" accesskey= "n" > 保存并输入写下一条(N) < / button >
< button onclick= "okClick('saveAndExit');" accesskey= "o" > 保存并退出(O) < / button >
< button onclick= "window.close();" accesskey= "c" > 取消(C) < / button >
< / div>
< div id= xviewbar style= "display:none;" >
< button onclick= "window.close();" accesskey= "x" > 关闭窗口(X) < / button >
< / div>
< / td> < / tr>
< / table>
< / BODY>
< !-- = = = = = = = = = = = = = = = Script area = = = = = = = = = = = = = = = = = = = = = = = -->
< SCRIPT LANGUAGE= "JavaScript" >
< !--
////////////////=====A.界面初始化=========
var formForInsert = false; //当前页面表单是否用于新建(否则为更新或查看)
function loadok () {
var id = XgetPara("id" ,"-1" ); //取url传递过来的对象键值,-1为新建对象
formForInsert = id = = "-1" ;
if (formForInsert){ nextbtn.style.display= "" ; } //新建时可“保存并新建下一条”
callServer("aop.Test" ,"getBook" , '&id=' +id
+ XgetFormDicsURL(bookform)); //取对象属性及表的元数据(字段定义)及记录行(更新时),XgetFormDicsURL取表单中的字典信息。
if (XgetPara("forview" )= = '1' ){ //表单仅用于查看
xviewbar.style.display= "" ; xeditbar.style.display= "none" ;
}
}
function on_getBook () {
if ((!formForInsert) & & buffer.book.length = = 0 ){
alert ("指定的Book已不存在!可能是另外的用户刚刚删除了该记录。" ); window .close (); return ;
}
XadjustControlSize(); //根据数据库的元数据调整各输入框的显示长短。如手工调整则注释本行
XnoValidateOnBlur(); //焦点离开任意输入框不作输入校验(即不作动态校验)。如需要动态校验特性,则注释本行。
buffer.book_colLabels= ['ID' ,'TITLE' ,'AUTHOR' ,'PUBLISHER' ,'PUBLISHDATE' ,'CONTENT' ,'PRICE' ,'NUMBER' ]; //各字段的显示名(中文名)。表单整体输入校验时用。
XinitFormWithData(bookform,"book" ); //充填表单。新增对象时,只初始化校验信息和输入字典;更新对象时,还充填各字段值
if ((document .location +"" ).indexOf ("book.html" )> 0 )
windowResize(460 ,600 );
}
////////////////=====B.用户动作响应=========
var okClickType = null; //保存类型:保存save、保存并输入下一条saveAndNext、保存并退出saveAndExit
function okClick (type) {
okClickType = type ;
if (Xvalidate(bookform)){ //系统自动完成的输入校验
//在此输入个性化的校验
var act = formForInsert ? 'addBook' : 'updateBook' ;
callServer("aop.Test" ,act,bookform); //提交表单到服务器
}
}
////////////////=====C.对话框或后台动作响应=========
function on_updateBook () {
if (opener != null & & typeof (opener .onUpdateBook)!= 'undefiend' )opener .onUpdateBook();
doNext();
}
function on_addBook () {
if (opener != null & & typeof (opener .onAddBook)!= 'undefiend' )opener .onAddBook();
doNext();
}
function doNext () {
if (okClickType= = "saveAndNext" ){
window .document .location = window .document .location ;
} else if (okClickType= = "save" ){
if (formForInsert){ //新建
if (typeof (buffer.id)= = 'undefined' ){ alert ("后端开发者:程序错误,未返回新记录的主键值。" ); return ; }
bookform.id.value = buffer.id; //回填主键
bookform.id.readOnly = true;
formForInsert = false; //转为更新状态
}
} else //保存并退出saveAndExit
window .close ();
}
////////////////=====D.自定义的输入校验及显示格式化函数=========
function shortDate (inStr) { return inStr.substring (0 ,10 ); }
setTimeout ('loadok()' ,1 );
//-->
< / SCRIPT>
< / HTML>
OK,所有前端代码都生成了,就两个文件books.html、book.html。不用任何JSP、XML以及配置文件。
4、运行新程序
在浏览器中访问:http://localhost/7wx/books.html ,出现一个图书列表界面:
这个列表页有以下特性:点击表头可按该列排序,可多选或全选数据行,翻页浏览 。
该页中按〈新建〉或〈修改〉按钮,就可以打开表单进行图书新增或修改:
这个表单默认有以下特性:带输入校验,包括非空检查,串长度检查,输入数据类型和精度检查;输入域中按<TAB>键或回车可以移到下一个输入域,日期域的选择 。
在本例中,我们并未手工编写任何代码,DBView基于数据库表BOOK的元信息,生成了Test.java、books.html、book.html三个代码文件,完全具备了我们要求的单表增删改查功能。下一节中,我们将通读这三个代码文件,并对程序进行改进。
阅读次数(今天): 1500 ( )
【编辑 】 【撤消 】
【打印 】
【关闭 】
最新评论
2008-06-20 18:44:40
admin
IP:202.43.146.*
to javaboy: DBView可产生父子表的增、删、修改代码,目前没有教程例子。 to 匿名: 在Studio的DataSource And JDBC 节点下配置数据源。
2008-06-20 14:10:03
匿名
IP:116.24.222.*
怎么连接数据库啊,比如mysql?
2008-06-19 15:15:13
javaboy
IP:220.173.107.*
有没有多表关联增、删、修改、查找的例子?
2008-06-18 13:29:12
admin
IP:202.43.146.*
查一下lib下的tools.jar的版本。 http://www.xjawa.org/content/bbs/article.page?id=195&keyword=tools.jar
2008-06-18 00:48:02
匿名
IP:116.230.4.*
Jetty是不是太老了啊?jdk1.5.0_11报错
2008-05-04 17:05:54
小凫
IP:124.16.138.*
啥时候出下一章,学习资源太少了。最好讲讲高级应用!
2007-10-24 08:10:48
desu
IP:218.83.108.*
studing ... ...
2007-08-29 20:55:38
匿名
IP:222.70.149.*
为什么没有下一篇呢?
2007-06-26 11:13:11
hnbk
IP:219.133.154.*
good ths
共有 9 条评论
在基于Ajax的论坛中 讨论Ajax技术只有我们能做到
过客留言
Bug报告
FAQ
AjaxWorld
AjaxWorld Magazine
OpenAjax
Ajax@Javaeye
J道社区
Ajax@IBM DW
Ajax@Sun
51CTO 技术区
Ajax中国
无忧AJAX
百度帖吧-Ajax
ajax中国(Blog)