上篇文章我们用Swing的知识做了一个关于数据备份和恢复的桌面应用,现在我们就着手做一个WEB应用。这个简单,JSP就可以搞定了。但是且慢,我们知道,HTTP协议是无状态的,传统的WEB应用不管是提交表单,还是超链接或者JS跳转都会刷新页面,这样如果页面操作稍微复杂一点,是很难在重新请求后还维持着原有状态的。那么我们看着Swing的窗口就会发呆:能不能将WEB应用做的跟桌面应用一样及时响应局部刷新呢?
我们想一想,Swing桌面应用为什么可以局部刷新?哦,Swing桌面应用中没有无状态的HTTP协议,也不存在WEB服务器,而Java语言又具有访问文件和数据库服务器的能力,所以完全可以通过Swing桌面组件的事件监听机制来动态更新用户界面。那么,我们想起来了,在WEB浏览器端的网页界面上,也可以使用一种带有事件驱动模型的语言:Javascript!它不是也可以操作DOM来更新网页吗?!可是,现在的问题在于,Javascript并不具有访问文件和数据库服务器的能力,也就是说,Javascript只是一种客户端的脚本语言,它没有办法得到存储在文件或者数据库上的动态数据,也没有办法将用户填充的数据存放到这些存储介质上。真是头疼,怎么办?Javascript?Java?恩。在WEB浏览器端可以利用Javascript的事件驱动机制,在WEB服务器端能够使用Java语言写JSP或者Servlet组件来访问数据库!这样的话,要是可以在Web浏览器端的Javascript代码中调用Web服务器上的Java代码,就像是Java代码就在浏览器中一样。那不就大功告成了吗?哈哈,有思路了。能够非常优秀地做到这一点的,就是一款Ajax的框架—DWR(Direct Web Remoting)!
Ajax(Asynchronous JavaScript and XML)我们应该不陌生了,它彻底颠覆了传统WEB应用的模式,极大地改善了用户体验。在Ajax之前,我们就是提交请求—等待—提交请求—等待的周而复始,有了Ajax之后,我们的操作和对服务器请求就可以异步地进行,再也不用枯燥地等待了。如果大家以前没有使用Ajax的经验并且对它有兴趣,请参阅拙作:http://blog.csdn.net/lenotang/archive/2008/07/22/2692926.aspx 。那么,什么叫异步呢?举一个生活中的例子:我们程序员写代码,写的口渴了就倒水喝,而在我们倒水的时候是无法继续写代码的。也就是说,写代码和倒水是同步(按照一定先后顺序)进行的。这也就是传统WEB应用的模式。但是,如果我们有一个很贤惠(注意,这里许老师所说的贤惠并不是指闲在家里什么都不会^_^!)的女朋友在身边,情况就完全不一样了。敲代码,口渴了,一个眼神或者一个响指,茶就到了嘴边。也就是说,茶是你女朋友帮忙在倒,她在倒茶的时候你的代码不用停下来。这就是异步(同一时间做多件事情)。Ajax应用里面就有个类似女朋友这样的角色—XMLHttpRequest对象!各位,现在体验到有女朋友的好处了吧。(当然,女朋友比较凶悍的,切勿模仿,否则,非但没有茶喝,头上还可能长丘陵!)
呵呵,我们来看看DWR吧!DWR包含2个主要部分:
1.一个运行在服务器端的Java Servlet,它处理请求并且向浏览器返回响应。
2.运行在浏览器端的JavaScript,它发送请求而且还能动态更新网页。
我们要想使用这个Ajax的框架,首先还是老规矩,导入第三方JAR包dwr.jar,然后在web.xml上做DwrServlet的配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>
org.directwebremoting.servlet.DwrServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
除此之外,我们还要做一个dwr.xml放在WEB-INF目录下,里面就可以注册类别,配置Javascript和Java的映射。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
"http://getahead.org/dwr/dwr20.dtd">
<dwr>
<allow>
<create creator="new" javascript="OperaDAO">
<param name="class"
value="com.wepull.model.OperationDAO">
</param>
</create>
</allow>
</dwr>
最后,我们就可以写一个HTML文件backupRestore.html来完成我们的工作(里面要include几个JS文件):
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试DWR操作数据库</title>
<style type="text/css">
body {
font-size: 12px;
font-family: "宋体", arial;
color:#33333;
}
table select {
width: 150px;
background: #cecece;
border: 5px solid red;
padding: 0px;
margin: 0px;
}
#showMsg {
width: 360px;
color: red;
}
.opeBtn {
font-size: 12px;
border: 1px solid #cecece;
cursor: hand;
}
.ope {
font-size: 12px;
border: 1px solid #cecece;
}
h1{
color:red;
}
</style>
<script type="text/javascript" src="/dwrDB/dwr/interface/OperaDAO.js"></script>
<script type="text/javascript" src="/dwrDB/dwr/engine.js"></script>
<script type="text/javascript" src="/dwrDB/dwr/util.js"></script>
<!--
<script type="text/javascript" src="js/OperaDAO.js"></script>
<script type="text/javascript" src="js/engine.js"></script>
<script type="text/javascript" src="js/util.js"></script>
-->
<script type="text/javascript">
//页面加载后,获取并显示全部数据库
window.onload = getDatabases;
function getDatabases(){
//在Web浏览器端的Javascript代码调用Web服务器上的Java代码,就像是Java代码就在浏览器中一样。这就是DWR提供的功能!
OperaDAO.getDatabases(callbackDb);
$("database").onchange=function(){
showDataTable(this.value);
DWRUtil.addOptions("showMsg", new Array("选择数据库"+this.value+",列出下属数据表!"));
$("bkdbBtn").disabled=false;
$("rsdbBtn").disabled=false;
$("dtdbBtn").disabled=false;
$("bakAllBtn").disabled=false;
};
}
//回调函数,参数为调用返回值。在这里我们可以动态更新网页。
function callbackDb(list){
DWRUtil.removeAllOptions("database");
DWRUtil.addOptions("database", list);
}
//显示全部数据表
function showDataTable(dbName){
OperaDAO.getTableNames(dbName,callbackTable);
$("dataTable").onchange=function(){
$("bakBtn").disabled = false;
DWRUtil.addOptions("showMsg", new Array("选择数据表"+this.value+",您可以执行备份的操作!"));
};
}
function callbackTable(list){
DWRUtil.useLoadingMessage("wait....");
DWRUtil.removeAllOptions("dataTable");
if(list==null||list==""){
DWRUtil.addOptions("showMsg", new Array("该数据库没有数据表!"));
$("bakBtn").disabled = true;
return;
}
DWRUtil.addOptions("dataTable", list);
}
//清空表格名称列表
function clearDataTable(){
$("dataTable").length=0;
}
//显示全部数据文件
function showDataFile(){
if($("database").value==""){
DWRUtil.addOptions("showMsg", new Array("请先选择对应的数据库!"));
return;
}
$("resAllBtn").disabled = false;
OperaDAO.getDataFilePath($("database").value,callbackFile);
$("dataFile").onchange=function(){
$("resBtn").disabled = false;
DWRUtil.addOptions("showMsg", new Array("选择数据文件"+this.value+",您可以执行恢复的操作!"));
};
}
function callbackFile(list){
DWRUtil.useLoadingMessage("wait....");
DWRUtil.removeAllOptions("dataFile");
if(list==null){
DWRUtil.addOptions("showMsg", new Array("该数据库没有对应的数据备份文件!"));
$("resBtn").disabled = true;
return;
}
DWRUtil.addOptions("dataFile", list);
DWRUtil.addOptions("showMsg", new Array("显示"+$("database").value+"数据库下对应的所有数据文件!"));
}
//备份数据表
function backupDataTable(cond){
var tbNames = new Array();
var dataTables = $("dataTable");
if(cond==1){
for(var i = 0;i<dataTables.length;i++){
tbNames[i]=dataTables[i].value;
}
}else{
var count = 0;
for(var i = 0;i<dataTables.length;i++){
if(dataTables[i].selected){
tbNames[count]=dataTables[i].value;
count++;
}
}
if(count==0){
DWRUtil.addOptions("showMsg", new Array("请选择您要备份的数据表!"));
return;
}
}
if(confirm("您确信要备份对应表的数据文件吗?")){
changeDisabled(true,true,true,true,true,true,true,true);
OperaDAO.backupTables($("database").value,tbNames,callbackBackup);
}
}
function callbackBackup(list){
DWRUtil.addOptions("showMsg", list);
changeDisabled(false,false,true,true,false,false,false,true);
}
//恢复数据文件
function restoreDataFile(cond){
var fNames = new Array();
var dataFiles = $("dataFile");
if(cond==1){
for(var i = 0;i<dataFiles.length;i++){
fNames[i]=dataFiles[i].value;
}
}else{
var count = 0;
for(var i = 0;i<dataFiles.length;i++){
if(dataFiles[i].selected){
fNames[count]=dataFiles[i].value;
count++;
}
}
if(count==0){
DWRUtil.addOptions("showMsg", new Array("请选择您要恢复的数据文件!"));
return;
}
}
OperaDAO.restoreFiles($("database").value,fNames,callbackRestore);
}
function callbackRestore(list){
DWRUtil.removeAllOptions("showMsg");
DWRUtil.addOptions("showMsg", list);
}
function clearMsg(){
DWRUtil.removeAllOptions("showMsg");
}
//备份整个数据库
function backupDatabase(){
if($("database").value==""){
DWRUtil.addOptions("showMsg", new Array("请先选择对应的数据库!"));
return;
}
if(confirm("您确信要备份整个数据库"+$("database").value+"?")){
$("bkdbBtn").disabled=true;
OperaDAO.backupDatabase($("database").value,callbackBkDB);
}
}
function callbackBkDB(list){
DWRUtil.addOptions("showMsg", list);
$("bkdbBtn").disabled=false;
}
//恢复整个数据库
function restoreDatabase(){
if($("database").value==""){
DWRUtil.addOptions("showMsg", new Array("请先选择对应的数据库!"));
return;
}
if(confirm("您确信要恢复整个数据库"+$("database").value+"?")){
$("rsdbBtn").disabled=true;
OperaDAO.restoreDatabase($("database").value,callbackrsDB);
}
}
function callbackrsDB(list){
DWRUtil.addOptions("showMsg", list);
$("rsdbBtn").disabled=false;
}
//分离数据库
function detachDatabase(){
if($("database").value==""){
DWRUtil.addOptions("showMsg", new Array("请先选择对应的数据库!"));
return;
}
if(confirm("您确信要分离数据库"+$("database").value+"?")){
$("dtdbBtn").disabled=true;
OperaDAO.detachDatabase($("database").value,callbackdetachDB);
}
}
function callbackdetachDB(list){
//分离完数据库刷新数据库列表
getDatabases();
clearDataTable();
DWRUtil.addOptions("showMsg", list);
}
//附加数据库
function attachDatabase(){
if($("attDBName").value==""){
DWRUtil.addOptions("showMsg", new Array("请先填写要附加的数据库名!"));
$("attDBName").focus();
return;
}
if(confirm("您确信要附加数据文件到数据库"+$("attDBName").value+"?")){
$("atdbBtn").disabled=true;
OperaDAO.attachDatabase($("attDBName").value,$("dFile").value,$("lFile").value,callbackattachDB);
}
}
function callbackattachDB(list){
//附加完数据库刷新数据库列表
getDatabases();
clearDataTable();
DWRUtil.addOptions("showMsg", list);
$("atdbBtn").disabled=false;
}
function judgeDBName(dbName){
//DWRUtil.addOptions("showMsg", new Array("正在填写..."+dbName.value));
for(var i=0;i< $("database").length;i++){
if($("database")[i].value==dbName.value){
$("atdbBtn").disabled = true;
return;
}
}
$("atdbBtn").disabled = false;
}
//改变每个按钮的可操作性
function changeDisabled(flag1,flag2,flag3,flag4,flag5,flag6,flag7,flag8){
$("bakBtn").disabled=flag1;
$("bakAllBtn").disabled=flag2;
$("resBtn").disabled=flag3;
$("resAllBtn").disabled=flag4;
$("bkdbBtn").disabled=flag5;
$("rsdbBtn").disabled=flag6;
$("dtdbBtn").disabled=flag7;
$("atdbBtn").disabled=flag8;
}
</script>
</head>
<body>
<center>
<h1>数据库操作关系重大,请您务必小心谨慎!</h1>
</center>
附加数据库名:
<input class="ope" type="text" name="attDBName" onkeyup="judgeDBName(this)">
<input class="ope" type="file" name="dFile">
<input class="ope" type="file" name="lFile">
<br>
<table>
<tr>
<td>数据库</td>
<td>数据表</td>
<td><input class="opeBtn" type="button" value="显示数据备份文件"
onclick="showDataFile()"></td>
<td><input class="opeBtn" type="button" value="清空信息显示区" onclick="clearMsg()"></td>
</tr>
<tr>
<td><select id="database" size="20">
</select></td>
<td><select id="dataTable" size="20" multiple>
</select></td>
<td><select id="dataFile" size="20" multiple>
</select></td>
<td><select id="showMsg" size="20" multiple>
</select></td>
</tr>
<tr>
<td colspan="4"><input id="bakBtn" class="opeBtn" type="button"
value="备份选择数据表" onclick="backupDataTable(0)" disabled> <input
id="bakAllBtn" class="opeBtn" type="button" value="备份全部数据表"
onclick="backupDataTable(1)" disabled> <input id="resBtn"
class="opeBtn" type="button" value="恢复选择文件"
onclick="restoreDataFile(0)" disabled> <input id="resAllBtn"
class="opeBtn" type="button" value="恢复全部文件"
onclick="restoreDataFile(1)" disabled></td>
</tr>
<tr>
<td colspan="4"><input id="bkdbBtn" class="opeBtn" type="button"
value="备份数据库" onclick="backupDatabase()" disabled> <input
id="rsdbBtn" class="opeBtn" type="button" value="恢复数据库"
onclick="restoreDatabase()" disabled> <input id="dtdbBtn"
class="opeBtn" type="button" value="分离数据库" onclick="detachDatabase()"
disabled> <input id="atdbBtn" class="opeBtn" type="button"
value="附加数据库" onclick="attachDatabase()" disabled></td>
</tr>
</table>
</body>
</html>
我这里为了方便起见,就把CSS和JS都写在上面的 HTML文件中,这里大家要注意,dwrDB是我们WEB工程的名字,OperaDAO.js要和dwr.xml上的配置一致。我们怎么知道DWR的配置无误呢?启动服务器,在浏览器的地址栏上输入:http://localhost:8080/dwrDB/dwr ,如果看到DWR框架能够识别OperaDAO就代表Ok了。大家可以看到DWR的使用是非常便利的,并且真的很神奇。不是吗?在我们的Javascript里面调用Java对象的方法轻而易举,它向使用者屏蔽了Javascript和Java,也就是客户端和服务器端之间的界限!使用一个回调函数就得到了服务器端Java方法的返回内容。是不是非常棒?!好了,大家要好好地揣摩一下代码,把这个理解了之后,也可以系统地学习一下DWR这个优秀的框架。下一篇文章,我们将看到DWR和Spring这两个优秀框架的强强联手!