编写操作 DB2 数据的 Web 应用程序(第 3 部分)
2009-11-06 00:00:00 来源:WEB开发网我想要更详细信息!
假如您曾经报告过任何一种结果,那么您可能以前听说过那句话。某些人希望得到高度概括的信息,而其他人希望得到更详细的信息。本部分开发一个提供详细信息的 Web 页面。可以从刚完成的概括信息页面上链接 Product 和 Platform 的详细信息。本节讲述了 Product.JSP 和 ProductCl servlet。Platform.JSP 和 PlatformCl servlet 很相似,这里就不再赘述了。Platform servlet 和 JSP 按照平台格式化平台上的数据,而不是按照产品去格式化产品上的数据。除了数据库查询和数据排序方面略有差异外,JSP 代码和 servlet 代码非常相似。
Product.JSP 的目的是显示新页面,解决大多数的格式化问题,再调用 ProductC1 servlet 填入要显示的数据。此外,数据取自 DB2 UDB 表。servlet 格式化行中的数据,以便于每行都与某个包含测试结果的产品相对应。
正如在 图 12 中所看到的,两个 JSP 几乎完全相同。实际上,通过少量额外的编码和参数传递工作,或许可以使用替代变量,让一个 JSP 起两种作用。第 2、10、12、15、23 和 25 行包含那些应当修改的参数。如果使 JSP 中的 TITLE 和 H1 字段相同,那么将只会有两处区别。至此,所有要做的工作由图中第 12 和 15 行显示的两个 servlet include 处理。
图 12:Product 和 Platform.JSP
1 <HTML><HEAD>
2 <TITLE>Debugger/OLT Test Results by Product
3 </TITLE></HEAD>
4 <body text="#FF0000" bgcolor="#FFFFFF"
5 link="#FF00FF" vlink="#F00000"
6 alink="#FF8080"
7 background="../resources/whtmarb.jpg">
8 <center>
9 <font color="#FF0000">
10 <H1>Debugger/OLT Test Results by Product
11 </H1></font>
12 <jsp:include page="../ProductCl" flush="true" />
13 </center></BODY></HTML>
14 <HTML><HEAD>
15 <TITLE>Debugger/OLT Test Results by Platform
16 </TITLE></HEAD>
17 <body text="#FF0000" bgcolor="#FFFFFF"
18 link="#FF00FF" vlink="#F00000"
19 alink="#FF8080"
20 background="../resources/whtmarb.jpg">
21 <center>
22 <font color="#FF0000">
23 <H1>Debugger/OLT Test Results by Platform
25 </H1></font>
25 <jsp:include page="../PlatformCl" flush="true" />
26 </center></BODY></HTML>
2 对 2 的做法
上面讲述了 Product 和 Platform JSP 文件。之所以把这一节称为“2 对 2”是因为 Product 和 Platform JSP 很相似。同样,ProductCl 和 PlatformCl servlet 也很相似。因为它们很相似,所以这里只讲述 ProductCl servlet 是如何工作的。PlatformCl servlet 留给读者去做。但是,首先展示一部分由 Product JSP 和 servlet 生成的 Web 页面。
要得到该页面,用户需要在前一页上选择产品,并按下与产品对应的按钮。
图 13:Product JSP 和 servlet Web 页面
图片看不清楚?请点击这里查看原图(大图)。
既然知道了输出是什么样子,那就研究一下 servlet,并看看它是如何生成该输出的。根据您的环境,您可能会注意到,在 TestResult、Product 或 Platform 页面显示之前会有相当长的延时。正如以前说过的,现阶段我们不关心性能 — 只是让所有的工作立刻进行。调试 WebSphere 和 DB2 的工作放到以后去做,同时可能还会有一些应用逻辑需要更改。我所做的、的确可以改进性能的一个步骤就是使我的表单改用‘GET’操作而不是使用 ‘POST’操作。这会使后面的 JSP 更快显示。
图 14:Product servlet
1 ...
2 public void doGet(HttpServletRequest httpServletRequest,
3 HttpServletResponse httpServletResponse)
4 throws ServletException, IOException {
5 httpServletResponse.setContentType("text/html");
6 PrintWriter pWriter = httpServletResponse.getWriter();
7 Enumeration enumeration = httpServletRequest.getParameterNames();
8 if (enumeration.hasMoreElements()) {
9 String string1 = (String)enumeration.nextElement();
10 String string2 = httpServletRequest.getParameter(string1);
11 try {
12 access.startAction();
13 } catch (SQLException e2) {
14 System.out.println(new StringBuffer("SQL Exception : ")
15 .append(e2).toString());
16 }
17 String astring1[] = access.getLastDateAndDriver();
18 String string3 = access.mapName(string2);
19 pWriter.println(string2 + "</h2>");
20 pWriter.println("<font color=\"#3366FF\">Each row in this table
21 represents a test run of the Debugger/OLT/Log
22 Browser driver built on the date shown.");
23 pWriter.println("<br>For a history showing the changes between
24 drivers click here : </font>");
25 pWriter.println("<FORM METHOD=\"post\"
26 ACTION=\"History.jsp\">");
27 pWriter.println("<INPUT TYPE=\"submit\"
28 NAME=\"PRODUCT\" VALUE=\""+string2 + "\"></form>");
29 pWriter.println("<table BORDER=5 width=\"100%\"
30 BGCOLOR=\"#CCCCCC\" >");
31
32 ...
33 access.queryTSTCASESTotals("PROD_ID", string3);
34 access.queryTSTRSLTS("PROD_ID", string3);
35 int i1 = access.numFieldsTSTCASES();
36 int j1 = access.numRowsTSTCASES();
37 int k1 = access.numFieldsTSTRSLTS();
38 int i2 = access.numRowsTSTRSLTS();
39 for (int j2 = 0; j2 < i2; j2++) {
40 String astring2[] = access.getRowTSTRSLTS(j2 + 1);
41 if (astring2 == null) {
42 pWriter.println("<tr><td>ERROR : retrieve a TSTRSLTS
43 row failed.</td></tr>");
44 break;
45 }
46 for (int k2 = 0; k2 < k1; k2++)
47 System.out.println("row["+j2+"] field["+k2+"]"+
48 astring2[k2]);
49 String string4 = astring2[1];
50 String astring3[] = access.getRowTSTCASESPlat(string4);
51 if (astring3 == null) {
52 pWriter.println("<tr><td>ERROR : retrieve a TSTCASES
53 row failed.</td></tr>");
54 break;
55 }
56 int i3 = Integer.parseInt(astring2[7]);
57 int j3 = Integer.parseInt(astring3[3]);
58 ...
59 int i13 = Integer.parseInt(astring2[28]);
60 int j13 = j3 + j4 + j5 + j6 + j7 + j8 + j9 + j10 + j11 + j12;
61 int k13 = i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 + i12 + i13;
62 pWriter.print("<tr><td>"+astring2[0] + "</td><td>" +
63 astring2[9]. substring(3, 16) + "</td><td>"
64 + astring2[1] + "</td><td>");
65 pWriter.print(astring2[4]+", " + astring2[6] + ", " +
66 astring2[5] + "</td><td>");
67 // Total TC's, passed, failed
68 pWriter.print(j13+"</td><td>"+i3 + "/" + i3 * 100 / j13 +
69 "</td> <td>" + k13 + "/" + k13 * 100 / j13 + "</td><td>");
70 // Java auto passed, failed
71 if (j4 > 0) pWriter.print(k4+"/" + k4 * 100 / j4 + "</td>
72 <td>" + i5 + "/" + i5 * 100 / j4 + "</td>");
73 else pWriter.print("---/0</td><td>---/0</td>");
74 ...
75
76 if (j8 > 0) pWriter.println("<td>"+k8 + "/" + k8 * 100 / j8
77 + "</td><td>" + i9 + "/" + i9 * 100 / j8 + "</td></tr>");
78 else pWriter.println("<td>---/0</td><td>---/0</td></tr>");
79 }
80 pWriter.println("</table>");
81 ...
第 1-6 行就不解释了,因为这是标准的 goGet 方法的头。第 7-10 行以前没有见过。红色文本就是我们以前未曾看到过的。我会在后面 图 18 中讲述这类代码、枚举、getParameterName 方法和 getParameter 方法。因此,这里就不再讲解了,除了说明参数是如何从 Product.JSP 传递给 ProductC1 servlet 的,及类似的参数是如何从 Platform.JSP 传递给其相应的 servlet 的。
第 17-24 行从 DB2 中获取最初的几段数据,即测试运行完成的最近日期和测试运行的驱动程序名称。在考虑 servlet 代码时,意识到其他人会调用 servlet 是重要的。因此,可能会看到标记好象不成对。下面几行就有这样的情形。有一个“</h2>”标记,但却看不到前面有任何以 <h2> 开始的开头部分。乍一看,好象是代码有错。但是,进一步研究,可以看到 Product.JSP 中包含 <h2> 标记。这两个文件的输出组合生成了完整的 Web 页面。头之后是提供给用户的几行文本信息。
接着是第 25-28 行中的 FORM 标记。第一次看到表单是在 图 5中。区别在于,在这个实例中,将通过表单把值传递给 History.JSP。该值就是我们希望显示其历史记录的产品名称。第 33-39 行显示对 DB2 进行查询,以检索各种所需产品的详细信息。参数 string3 实质上是数据库中的产品标识符。方法 queryTSTCASESTotals、queryTSTRSLTS、queryTSTRSLTS、numFieldsTSTCASES、 numRowsTSTCASES、numFieldsTSTRSLTS 和 numRowsTSTRSLTS 都检索不同值,这些值用来控制后面对数据库中各行信息的数据库查询。for 语句定义了行数以及通过 getRow 方法循环检索行信息的次数。
开发 Web 应用程序时,转储关键点上的某些信息有时非常有用,这样可以确保正在处理的数据是正确的。第 46-48 行的目的就在于此。该循环把刚刚检索过的行数据的列值转储到标准输出。应用程序测试完成后,应该禁用此代码。在实际的应用程序中,如果有一些提供更多调试类型信息的机制,同时可以打开和关闭该机制,那就很不错了。这称为跟踪,是复杂的应用程序中一项无以伦比的功能。DB2 UDB 有各种能独立用于帮助确定 DB2 中正在发生什么的跟踪。同样,WebSphere 也有自己的方法跟踪 WebSphere 应用服务器的执行并为用户提供检查跟踪信息的方法。
要能够计算百分比,需要知道测试运行的次数和测试案例的总数。总数存储在 DB2 的 TSTCASES 表中。第 49-55 行从数据库中检索一组总数。还检查错误以保证的确返回了总数。稍后我们就不打算用不存在的总数计算百分比了。被 0 除对于程序而言是没有意义的。第 56-61 行获得一行用字符串信息表示的返回数据,并把数据转换为整型,以便对其进行数学运算。请注意 j13= 和 k13= 语句。这些是一些测试结果的总计,以确定各种总数,例如所有成功测试案例的总数,或所有失败的测试案例的总数。
最后,第 62-78 行获取特定产品的单独测试结果,以及计算得出的总数和百分比。然后格式化这些结果。现在把新代码安置到 WebSphere 中很容易,这里就不多说了,只要对这些文件:Product.jsp、Platform.jsp、ProductC1.class 和 PlatformC1.class 重复前面对 JSP 和 servlet 的步骤。请记住,.class 文件由 .java 文件生成。在 TestResults 页面上单击产品或平台按钮进行测试。
现在也许是了解 AAT 是什么样子的好契机。 图 15a显示了到目前为止所定义的 Web Components。
注:如果没有行能匹配显示条件,平台和/或产品表可能不会显示。
图 15a:AAT Web Components
图片看不清楚?请点击这里查看原图(大图)。
关于多平台和交付
很多产品中都提供了调试器和“对象级别跟踪器(Object Level Trace)”工具,这些工具有多平台的许多发行版。要追踪这些产品在何时及何地交付很困难。“驱动程序交付历史记录”链接提供了可以显示该信息的静态 Web 页面。该信息可以在简单的 HTML 文件中找到,它不是数据库的一部分。要在每次发行后更新该文件,需编辑 HTML 文件并添加一些项。该信息还可以放入数据库,然后编写 servlet 抽取和显示该信息。由于这是简单的 HTML,所以代码就不在这介绍了。
但是,简单并不意味不重要。对于那些处理客户问题的开发人员而言,客户问题越罕见,该信息就越至关重要。要完全再现所遇到的问题,就要确保创建完全相同的环境。因此,就需要知道驱动程序级别、构建日期、JDK 级别、编译器级别和操作系统级别。
注:使用 AAT 将 driverhistory.html 添加到资源文件清单。
关于驱动程序历史记录
驱动程序历史记录一直都是令人感兴趣的测试主题。查看测试结果时,用当前的结果与以前驱动程序测试结果的一些历史记录进行比较是很有用的。当开发越是接近完成,测试中发现的问题就应该越少,这形象地刻画了我们的代码质量。类似的,考虑当前的测试结果,并将其与去年的、上一个发行版和最新的修订包做比较是很有益的,这样可以知道产品的开发状态。
由 Platform 和 Product 的 servlet 和 JSP 显示的信息将在本文最后一节讲解。本节描述另一种类型的历史记录,即通过展示驱动程序之间的变化,进行驱动程序间的数字化比较。Driver History 按钮调用 DriverHistory JSP 以允许进行这种类型的比较。我知道这并不是 图 3中的那一对 JSP/servlet,但是,“变化是生活的调味品”。由于 DriverHistory JSP 很小且很简单,就不在这里讲述了。所有内容都包含在这个 JSP 中。 图 14 包含了由 DriverHistory JSP 调用的 HistoryC1 servlet 的关键代码块。
图 15:HistoryCl servlet
1 ...
2 String astring1[] = access.getLastDateAndDriver();
3 String string3 = access.mapName(string2);
4 printWriter.println(string2 + "</font></p>");
5 printWriter.println("<font color=\"#3366FF\">Each
6 row in this table represents the
7 changes in a test run of the Debugger/OLT/Log Browser compared to the
8 previous run.</font>");
9 printWriter.println("<br><table BORDER=5
10 width=\"100%\"
11 BGCOLOR=\"#CCCCCC\" >");
12 printWriter.println("<tr BGCOLOR=\"#99FF99\"><td>Driver</td><td>Plat.</td>
13 <td>JDK, O/S,<br>Compiler</td>");
14 printWriter.println("<td>Passed/%</td><td>failed/%</td><td>run/%</td>
15 <td>Java Auto pass/%</td><td>Java Auto fail/%</td>");
16 printWriter.println("<td>C/C++ Auto
17 pass/%</td><td>C/C++ Auto fail/%</td>
18 <td>Java Man. pass/%</td><td>Java Man. Fail/%</td>");</
19 printWriter.println("<td>UI pass/%</td><td>UI fail/%</td>
20 <td>Misc pass/%</td><td>Misc fail/%</td>");
21 printWriter.println("<td>64bit Auto
22 pass/%</td><td>64bit Auto fail/%</td>
23 <td>64bit Man. pass/%</td><td>64bit Man. fail/%</td></tr>");
24 access.queryTSTCASESTotals("PROD_ID", string3);
25 access.queryTSTRSLTS("PROD_ID", string3);
26 int i2 = access.numRowsTSTRSLTS();
27 if (i2 == 1) {
28 printWriter.println("<tr><td>
29 </td></tr></table>");
30 printWriter.print("<p>There is either only one row of history, in which case
31 you can see it on the previous screen, or there was an error
32 retrieving/formatting the information.</p>");
33 } else {
34 for (int j2 = 0; j2 < i2 - 1; j2++)
35 {
36 printWriter.println("<tr>");
37 String astring2[] = access.getRowTSTRSLTS(j2 + 1);
38 String astring3[] = access.getRowTSTRSLTS(j2 + 2);
39 if (astring2 == null || astring3 == null) {
40 printWriter.println("<td>ERROR : retrieve a TSTRSLTS row
41 failed.</td></tr>");
42 break;
43 }
44 String string4 = astring2[1];
45 String string5 = astring3[1];
46 if (!string4.equalsIgnoreCase(string5)) continue;
47 String astring4[] = access.getRowTSTCASESPlat(string4);
48 if (astring4 == null) {
49 printWriter.println("<td>ERROR : retrieve a TSTCASES row failed.
50 </td></tr>");
51 break;
52 }
53 int k2 = Integer.parseInt(astring3[7]) - Integer.parseInt(astring2[7]);
54 int i3 = Integer.parseInt(astring4[4]);
55 int j3 = Integer.parseInt(astring3[7]) - Integer.parseInt(astring2[8]);
56 int k3 = Integer.parseInt(astring3[10]) - Integer.parseInt(astring2[10]);
57 int i4 = Integer.parseInt(astring4[4]);
58 int j4 = Integer.parseInt(astring3[10]) - Integer.parseInt(astring2[11]);
59 int k4 = Integer.parseInt(astring3[12]) - Integer.parseInt(astring2[12]);
60 ...
61 int i12 = Integer.parseInt(astring4[13]);
62 int j12 = Integer.parseInt(astring3[27]) - Integer.parseInt(astring2[27]);
63 int k12 = Integer.parseInt(astring3[28]) - Integer.parseInt(astring2[28]);
64 int i13 = k3 + k4 + k5 + k6 + k7 + k8 + k9 + k10 + k11 + k12;
65 int j13 = i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11 + i12;
66 int k13 = j13 - (k2 + i13);
67 printWriter.print(astring2[0] + "</td><td>" + astring2[1] + "</td><td>");
68 printWriter.print("," + astring2[6] + "," + astring2[5] + "</td><td>");
69 if (k2 > 0) printWriter.print("/" + k2 * 100 / j13 + "</td><td>");
70 else printWriter.print("---/0</td><td>");
71 if (i13 > 0) printWriter.print("/" + i13 * 100 / j13 + "</td><td>");
72 else printWriter.print("---/0</td><td>");
73 if (k13 > 0) printWriter.print("/" + k13 * 100 / j13 + "</td><td>");
74 else printWriter.print("---/0</td><td>");
75 ...
76 if (i8 > 0) printWriter.println(j8 + "/" + j8 * 100 / i8 + "</td><td>" + k8 +
77 "/" + k8 * 100 / i8 + "</td></tr>");
78 else printWriter.println("<td>---/0</td><td>---/0</td></tr>");
79 }
80 printWriter.println("</table>");
81 ...
82 }
输出的并不是真正的驱动程序测试结果,而是驱动程序之间的变化,或者说是驱动程序之间的增量测试结果。我要做的,但还没做的是用图表显示比较结果。可以运行浏览器内部的 applet 实现该步骤 — 使用 Java2D 的绘制类或用等宽字体以不同背景色绘制几行空格。这样可以模拟条形图。亲自尝试一下,对您而言这是个很好的练习机会。如果看了输出数据,会注意到 servlet 代码中有一些逻辑错误。由于优秀的 :-) 出错处理功能,逻辑不会导致故障或异常终止。对读者而言,修正错误也是一个极佳的锻炼机会。
现在,回去看一下 servlet。由于大量 servlet 程序块是重复的,所以就不考虑了。第 2-23 行获取最新的驱动程序日期和时间,格式化该信息用于显示,创建表并显示表的标题行。第 24-33 行指明 DBaccess 类从数据库中查询一些总数,检索总的行数,检查是否多于一行,如果不,则发出一条消息结束处理。如果数据库中仅有一行数据,那么仅有一项测试运行,因而没有以前的运行可供比较,所以在这里停止构建表。第 34-43 行启动循环,按照测试得到的行进行循环。一次抽取两行,当前行和下一行。我们需要能相互比较的两行。检查每一行以确保数据已经返回,然后可以安全地开始比较。
第 44-52 行从被检索的两行的每行中抽取产品名称,并对它们进行比较,以确保它们是相同的。我们不希望用不同的产品做比较。如果产品不同,那么循环就反复进行,比较接下来的两行,其中的“接下来的两行”是指当前两行的第二行和一个新行。这里要说的是,这只是一个示例。该示例在所有地方都能很好地工作,除了这一点,也就是说,History servlet 是不完整的,未考虑所有可能性。
例如,数据库可能包括如下几行:product1、product2、product1、product2 ... 如果是这种情形的话,最后显示的将是空表,因为相邻行的产品比较始终都是失败的。真正应该发生的是,给定的一行(比方说 product1)应该与后面所有的行比较,以查找是否有任何匹配行。如果找到匹配行,就完成比较。所以第一个 product1 与第三行(第二个 product1)比较,比较成功后,测试结果中的区别会计算和显示在表中。
实际情况下,就要考虑这种情形。这个问题有多种可能的解决方案。例如,对抽取几行数据的查询添加 ORDER BY 子句,就可以按照产品对数据分组,这使所有的 product1 行集中在一起,而所有的 productX 行集中在一起,那么上面的比较就可以正确地进行。另一种需要考虑的情况是,该示例要将 AIX 平台上的测试结果与 Solaris 平台或 Linux 平台上的结果进行比较。这可能也是您不希望看到的一种情形,这样,就要添加逻辑以便处理这种情况。
第 53-66 行不完整,遗漏了很多行,不过这些行只是为每列测试信息重复相同的逻辑。对于正在比较的两行,每列都得比较,以找到两项测试之间的差值。由 DBaccess 类返回的字符串需要转换成整型,相减后确定差值。对于 DBaccess 本应有的更好设计是将数据库中的信息作为整型查询,而不是作为字符串型查询,这样就无需进行字符串型到整型的转换。这个工作留给读者练习。
第 54-56 行确定所有相似列的总数。分别计算所有成功和失败测试的总数,未运行的测试可以用测试总数减去前面计算所得的已测试总数得到。
最后,57-82 行获得比较结果,并对其格式化,放入表中用于显示。这里去除了一部分重复代码。最后,通过 </table>标记结束表。这里没有讲述的一些其它代码包括 servlet 设置和出错处理,它同本示例中其它 servlet 非常相似。
安装 History JSP 和 HistoryCl servlet,因为您已经知道如何做(现在,闭着眼睛您可能都可以做)。
图 16:样本图表
January | 图片看不清楚?请点击这里查看原图(大图)。 |
February | 图片看不清楚?请点击这里查看原图(大图)。 |
March | 图片看不清楚?请点击这里查看原图(大图)。 |
April | 图片看不清楚?请点击这里查看原图(大图)。 |
May | 图片看不清楚?请点击这里查看原图(大图)。 |
June | 图片看不清楚?请点击这里查看原图(大图)。 |
1 <table width="235">
2 <tr><td width="50%">January</td>
3 <td><img SRC="http://tech.ddvip.com/2009-11/resources/SPACE.JPG" NOSAVE height=20 width=75></td></tr>
4 <tr><td>February</td>
5 <td><img SRC="http://tech.ddvip.com/2009-11/resources/SPACE.JPG" NOSAVE height=20 width=125></td></tr>
6 <tr><td>March</td>
7 <td><img SRC="http://tech.ddvip.com/2009-11/resources/SPACE.JPG" NOSAVE height=20 width=20></td></tr>
8 <tr><td>April</td>
9 <td><img SRC="http://tech.ddvip.com/2009-11/resources/SPACE.JPG" NOSAVE height=20 width=75></td></tr>
10 <tr><td>May</td>
11 <td><img SRC="http://tech.ddvip.com/2009-11/resources/SPACE.JPG" NOSAVE height=20 width=110></td></tr>
12 <tr><td>June</td>
13 <td><img SRC="http://tech.ddvip.com/2009-11/resources/SPACE.JPG"NOSAVE height=20 width=60></td></tr></table>
前面几段提到过用图表的形式显示数据。这里是一个小的样本,可以看出它看上去是什么样子的。左栏显示图表,而右栏显示生成图表的代码。蓝色的图形条只是小的位图,可以水平延伸至所希望的长度。请注意第 3、5、6、9、11 和 13 行。每种情况下显示的图片都是相同的 SPACE.JPG。高度都是 20,只是宽度不同。这是根据 DB2 中抽取的数据动态生成的那部分结果。由于文本的本性 — 通常是从左向右运行,因而创建水平条形图比垂直条形图要容易。如果打算创建用于 Web 的图表,那么一定要记住这一点。
关于定制报告
某个用户来到该网站,运行应用程序,看了看上面描述的测试结果的汇总表,又看了看也是在上面描述的产品和平台的特定测试结果,还是不满意。接下来该怎么办呢?答案是定制报告。允许用户自己决定他们所想要看到的信息。因为这是一个示例,并非真正的应用程序,因而定制报告不会象它应当有的那样灵活。在该示例中,我让用户选择产品和平台,并显示该特定产品和平台组合的所有可用的测试状态。不像在产品表中那样,用户会看到一个产品在所有平台上的所有测试结果,也不像在平台表中那样,用户会看到一个平台上所有产品的所有测试结果,定制表显示了特别请求的产品和平台。
图 17:Custom.JSP
1 <HTML><HEAD><TITLE>Debugger/OLT Custom Query</TITLE></HEAD>
2 <body text="#FF0000" bgcolor="#FFFFFF" link="#FF00FF" vlink="#F00000"
3 alink="#FF8080" background="../resources/whtmarb.jpg">
4 <center><table><tr><td>
5 <IMG SRC="http://tech.ddvip.com/resources//WasBanner.gif"><td><td>
6 <font color="#FF0000"><H1>DD/OLT Custom Query</H1></font></td></tr>
7 </table>
8 <table><tr><td><font color="#FF0000"><font size=+3>Product
9 </font></font></td>
10 <td><font size=+3><%= request.getParameter("product")%>
11 </font></td>
12 <td><font color="#FF0000"><font size=+3 Platform
13 </font></font></td>
14 <td><font size=+3><%= request.getParameter("platform")%>
15 </font></td></tr>
16 </table>
17 <table BORDER=5 width="100%" BGCOLOR="#CCCCCC">
18 <tr BGCOLOR="#99FF99"><td>Driver</td><td>Date</td><td>JDK, O/S,
19 <br>Compiler</td>
20 <td>Total TC's</td><td>Passed/%</td><td>failed/%</td>
21 <td>Java Auto pass/%</td>
22 <td>Java Auto fail/%</td><td>C/C++ Auto pass/%
23 </td><td>C/C++ Auto fail/%</td>
24 <td>Java Man. pass/%</td><td>Java Man. Fail/%
25 </td><td>UI pass/%</td>
26 <td>UI fail/%</td><td>Misc pass/%</td><td>Misc fail/%
27 </td>
28 <td>64bit Auto pass/%</td><td>64bit Auto fail/%
29 </td><td>64bit Man. pass/%</td>
30 <td>64bit Man. fail/%</td></tr>
31 <jsp:include page="../CustomCl" flush="true" />
图 9:TestResults.jsp中的第 24-28 行显示了一个用户可以输入选择的产品和平台的表单。如这里所示,按下 Submit 按钮将调用 Custom.jsp。这里有个新特性,就是参数从一个 JSP 传递给另一个 JSP。 图 17 中,第 1-7 行格式化并显示页面标题,这和其它所有的页面都相似。第 8-16 行格式化并显示通过调用 JSP 传递的产品和平台信息。注意,<%=request.getParameter("product")%> 语句,以及类似的 <%=request.getParameter("platform")%> 语句。这些新标记获取“product”和“platform”参数,并“按现状”插入到显示中,而无论当前是什么位置。因此,这些标记的放置很重要。表中一行的第一个单元格显示 Product,同一行的第二个单元格显示产品参数。第三和第四个单元格类似;它们将显示填入的平台字符串及其值。
第 17-30 行格式化并显示表的标题,当第 31 行调用 CustomC1 servlet 从 DB2 获取所需的信息,并将信息添加到表中时,该表将显示定制查询的结果。照常要从样本向 WebSphere 复制代码。像上面所述的所有其它 JSP 和 servlet 样本那样完成该过程。
图 18:Custom CL servlet
1 public void doGet(HttpServletRequest httpServletRequest,
2 HttpServletResponse httpServletResponse)
3 throws ServletException, IOException {
4 httpServletResponse.setContentType("text/html");
5 PrintWriter pWriter = httpServletResponse.getWriter();
6 String str1 = httpServletRequest.getParameter("product");
7 String str2 = httpServletRequest.getParameter("platform");
8 ...
下面将讲述一部分 CustomC1.java,因为它也包含一些处理参数的新逻辑。与 JSP 处理产品和平台参数一样,当需要 servlet 查询 DB2 时,servlet 也必须处理它们。第 1-5 行就是普通的‘doGet’servlet 方法代码,用于建立 printWriter 对象,无论 servlet 写到哪里,printWriter 都将终止 Web 页面上显示的内容。
第 6-7 行代码表明获取参数。在本例中,我们知道参数名称。有时 servlet 可能不知道名称或参数,这种情况下,可以使用 Java 枚举。调用 getParameterNames() 方法,并把结果存储为对象。接下来检查以确保的确存在一些参数。如果参数存在,那么除了象在本例中那些参数名不能硬编码以外,第 6-7 行显示那些参数的抽取。它就是从枚举返回的值。此外,因为这是个示例,没有包含全部的出错检测代码。其余的代码与运行 DB2 查询的 Product 和 Platform servlet 类似,但是是基于 2 个标准 — 产品和平台,而不只是 1 个标准,因此它是更受限的查询。然后通过把结果数据写到 printWriter 对象,对其进行格式化并显示。
输入数据
图 19:文件 update.html 的输出
图片看不清楚?请点击这里查看原图(大图)。
我们始终都从数据库中抽取数据,并以各种格式显示。要能抽取数据,首先必须将其插入数据库。有很多方法可以完成该工作,例如直接通过 DB2 INSERT 命令,或通过 DB2 命令中心(Command Center)对于那些不熟悉 SQL 语法的人更容易,或通过 Web 应用程序。最合乎逻辑和对用户友好的方法是在 Web 应用程序中提供一个界面,测试人员可以通过同样的测试结果界面在其中输入测试结果。 图 5 显示了调用 Update.html 的主 Web 页面。
Update.html 又是一个表,测试人员可以输入各种测试结果和关于进行了哪些测试的信息,并可以按下 ADD 或 UPDATE 按钮。ADD 将向数据库中添加一条新的测试记录,而 UPDATE 会更改一条现有记录的值。该页面显示在 图 19中。看一下该页面的 HTML 代码,会发现我在表中使用了表,而所有的表都在一个表单中。嵌套的表允许字段组按照我的要求在页面上进行对齐,而无需调整页面大小。这些表都带有一个表单,以便于测试人员输入测试结果的区域可供查询,并传递给 Update servlet 插入数据库中。 图 20 中包含一小段 Update.html 页面的代码,它显示了对来自 HTML 的参数的使用。稍后会研究信息是如何传递给 servlet 的。
图 20:Update.html 代码片段
ion>
1 <FORM METHOD="post" ACTION="UpdateResults.jsp">
2 <TABLE><TR>
3 <TD><b><font color="#000000">Product *</font></b></TD>
4 <TD><INPUT TYPE="text" NAME="product" ID="product"
5 SIZE="30" MAXLENGTH="40"></TD></TR>
6 ...
7 <TABLE><TR><td><INPUT TYPE="submit" NAME="Add" ID="Add"
8 VALUE="Add">{input}</td>
9 <td><INPUT TYPE="submit" NAME="Update"
10 ID="Update" VALUE="Update">{input}</td></TR>
11 </TABLE></TD></TR></TABLE></FORM>
12 ...
第 1 行显示了表单的构造,当按下 ADD 或 UPDATE 按钮时,它指定要调用的 JSP。按下 ADD 按钮或 UPDATE 按钮,然后调用 servlet。第 4 和第 5 行包含 INPUT 标记,用于表示该区域长度为 30 格,且测试人员最多可输入 40 个产品字符。这意味着如果输入超过 30 个字符,字段会滚动。该值存储在‘product’变量中。第 7 和第 8 行说明 ADD 按钮的处理方法。注意,TYPE 标记表示 submit。这表明它是一个按钮,而第 4 和第 5 行中的类型是 text,表明用户可以在该区域输入文本、字符串数据。同样,第 9 和第 10 行说明 UPDATE 按钮的处理。
图 21:Update servlet 代码片段
1 public void doGet(HttpServletRequest httpServletRequest,
2 ...
3 String string1 = httpServletRequest.getParameter("product");
4 String string2 = httpServletRequest.getParameter("platform");
5 ...
6 String string27= httpServletRequest.getParameter("machine");
7 if (string1 == null || string1.length() == 0 || string2 == null ||
8 string2.length() == 0 || string3 == null || string3.length() == 0
9 || ... || string6.length() == 0) outputError(printWriter, 1);
10 ...
11 String string29 = httpServletRequest.getParameter("Add");
12 String string30 = httpServletRequest.getParameter("Update");
13 ...
14 if (flag1) { // doing an ADD
15 if (string7.length() == 0) string7 = "0";
16 if (string8.length() == 0) string8 = "0";
17 ...
18 if (string27.length() == 0) string27 = "------";
19 access.queryTSTCASESProductTotals(string28, string2);
20 ...
21 string31 = string31.concat(
22 "INSERT INTO TSTRSLTS VALUES('")
23 .concat(string3).concat("','");
24 string31 = string31.concat(string2)
25 .concat("','").concat(string28).concat("','");
26 ...
27 string31 = string31.concat(string26).concat(")");
28 } // end of ADD
29 else { // doing an UPDATE
30 string31 = "UPDATE TSTRSLTS SET (";
31 string32 = ")=(";
32 if (string7.length() > 0) {
33 string31 = string31.concat(
34 "JAVA_MANUAL_PASSED,");
35 string32 = string32.concat(string7).concat(",");
36 }
37 ...
38 if (string32.substring(string32.length() - 1).equals(","))
39 string32 = string32.substring(1,string32.length() - 1);
40 string32 = string32.concat(") WHERE PROD_ID='")
41 .concat(string28).concat("' AND platform='");
42 string32 = string32.concat(string2)
43 .concat("' AND driver='").concat(string3);
44 string32 = string32.concat("' AND JDK_LEVEL='")
45 .concat(string5).concat("' AND OS_LEVEL='");
46 string32 = string32.concat(string4)
47 .concat("' AND compiler='").concat(string6)
48 .concat("'");
49 string31 = string31.concat(string32);
50 } // end of UPDATE
51 printWriter.println("<HTML><HEAD><TITLE>");
52 printWriter.println("Debugger/OLT Update Database");
53 printWriter.println("</TITLE></HEAD><BODY>");
54 printWriter.println("<center>");
55 printWriter.println("<font color=\"#FF0000\">
56 <H1>Debugger/OLT Update Database</H1>
57 </font></center><p>");
58 printWriter.print("Requested action : ");
59 if (flag1)
60 printWriter.println("<b>Add an entry</b></p>");
61 else
62 printWriter.println("<b>Update an entry</b></p>");
63 printWriter.println(string31 + "</b></p>");
64 printWriter.print("<p>Result: <b>");
65 boolean flag2 = access.submitRequest(string31);
66 ...
67 private void outputError(PrintWriter printWriter, int i) {
68 printWriter.println("<HTML><HEAD><TITLE>");
69 printWriter.println("Debugger/OLT Update Database Error");
70 printWriter.println("</TITLE></HEAD><BODY>");
71 printWriter.println("<center>");
72 printWriter.println("<font color=\"#FF0000\">
73 <H1>Debugger/OLT Update Database Errors</H1>
74 </font><br><br><p>");
75 switch (i) {
76 case 1:
77 printWriter.println("One of the required parameters
78 was not entered. Please enter all fields
79 marked with an asterick");
80 break;
81 case 2:
82 printWriter.println("Product name does not exist in
83 the database.");
84 break;
85 default:
86 printWriter.println("Some error occurred, I dont
87 know what it is.");
88 break;
89 }
90 printWriter.println("</p></center></body></html>");
91 }
接下来讲述的是 Update servlet 部分。由于含有许多重复代码,它显得相当冗长,所以本文只在这里讲解一部分。 图 21 中的每个字段在可以用于 SQL 语句之前(向数据库中添加一行或更新现有行)都得检查一下。
本代码样本包含 27 个字符串,如第 3-6 所示,标号为 string1 到 string27。string 可能不是好的变量名,但是这里就是这样命名的。每个字符串对应输入 HTML 表单的一个字段,该表单正好包含 27 项字段。本来利用循环对此编码是个好方法。但是,由于每次调用的参数(product、platform 和 machine)都不同,因而循环很困难,但不是不可能。
一旦读取参数,就要检查所需的字段是否为空。第 7-9 行完成这个任务。它检查每个字段,确保对象不为空,然后确保对象的长度不为 0。如果任何所需的字段为空,那么 servlet 会显示出错页面,同时不更新数据库。通过对 outputError(printWriter,1) 的调用生成出错页面;通过参数 1,表明出错类型,从而把消息显示在出错页面上。第 11-14 行获取 Add 和 Update 按钮的值。如果两个按钮中仅按下一个,就只有一个按钮有值。确定哪个有值的逻辑就不说明了,但是该逻辑设置了 flag1。根据 flag1,添加还是更新部分的代码就会在后面进行调用。
第 15-27 行处理添加请求。在添加过程中,需要把所有参数传递给数据库,而不只是用户指定了值的参数。然后,必须检查每个参数,如果未传递任何参数,值必须设为 0。第 21-27 行开始构建 DB2 SQL 语句。这些行包含 INSERT INTO TSTRSLTS VAUES(a,b,c,d,....),其中 a、b、c 和 d 是我们希望插入的值。当 21-27 行结束时,该部分构建的 string31 变量将变成完整的插入语句。第 36 行是 UPDATE 部分的起始点。更新行使用的是不同的 SQL 语句,因而与添加行的构建也不同。更新语句的形式是 UPDATE TSTRSLTS SET(f1, f2, f3,...)=(v1,v2,v3,...) WHERE PROD_ID=? AND platform=? AND driver=? AND JDK_LEVEL=? AND OS_LEVEL=? AND COMPILER=?。WHERE 之后的部分(第 40-49 行显示)定义了要更新哪一行的条件。这些字段在 HTML 表单上是必需的,因为需要用它们来唯一确定更新哪一行。语句的 SET(....)部分定义了哪列要设置新值。=(...)部分说明将会是什么值。这里只更新指定列,其它所有列值与更新前保持一致。
回到第 30-35 行,可以看到一个‘if’语句,它检查 string7 的长度是否大于 0。如果大于 0,可以知道用户在 HTML 表单中向该区域输入了值,因此,我们希望添加 SQL 语句,以确保值在数据库中被更新。这类‘if’语句会重复用于 string7 到 string28。作为示例,我们假定 HTML 表单的 Java_manual_passed 区域中输入了值。当 string7.length() 的检查完成后,可以看到它大于 0,然后执行接下来的两条语句。将参数 string31 设为 UPDATE TSTRSLTS SET (,附加 JAVA_MANUAL_PASSED,。将参数 string32 设为 )=(,附加 string7。所以,到此为止语句就变成 UPDATE TSTRSLTS SET (JAVA_MANUAL_PASSED,)=( 'string7',其中 string7 是它的真实值,而不是文本的 string7。这是 string31 和 string32 的合并。实际上是由 40-49 行完成的。由于有其它字段要检查,并要合并各个字符串,所以现在该工作不能完成。
因为跳过了第 38-39 行,现在回去看一下。注意,前一段中的 string31 以逗号结束。这是由于在字符串后附加了值,并且不知道哪个字符串是最后一个。所以附加工作完成后,需要返回并除去最后的逗号。那就是第 38-39 行的工作。它检查字符串中最后的字符,如果是逗号,就把字符串的长度缩短一个字符,从而消除逗号。
下面是第 59-64 行,用于设置 Web 页面以显示更新结果。它显示执行的是 ADD 操作还是 UPDATE 操作,然后显示要执行的 SQL 命令。接着是执行命令的结果。实际的数据库更新工作由第 65 行调用 submitRequest 方法完成。一些根据布尔型变量 flag2 是设为 true 还是 false,或者根据 submitRequest 方法调用是否抛出异常来处理方法调用的返回值的代码在这里没有显示。
最后是第 50-64 行的 outputError 方法。注意,该方法也创建用于显示的新 HTML 页面。这是因为该方法在前面提到过的黑体文本有机会创建页面之前就被调用。例如在出错情况下,会显示出错页面,同时进程会停止,这样,就不会执行请求的更新,因此不会调用 servlet 中通常创建页面的部分。第 75-88 行显示了实际的出错消息。我们需要从样本向 WebSphere 目录树拷贝代码,并像以前章节中所做的那样编译 Java 块。如此重复上述步骤,把 UpdateResults.JSP 和 UpdateCl servlet 及 Update.html 文件添加到 AAT 工具中。一定要记住,当导入的是 .class 文件而非 .java 时,首先要编译 servlet。还要把 servlet 和 JSP 添加为 Web Componencts,再添加 servlet 映射。保存文件,转到 WebSphere Admin console,删除旧应用程序,装入新应用程序。重新生成 Web 插件,通过 control panel-> Service 来停止/重新启动 HTTP 服务。完成了!现在所有东西都可工作了:每个 HTML 页面、JSP、servlet、声音、图形和那些添加的但已经记不起来的所有东西。
遗漏了什么吗?
如 图 3和 图 7 所示,相信现在有关 Web 应用程序的所有方面都讲述到了,除了最后一条 — Web Links。它们只是调试器和“对象级别跟踪器”的开发人员和测试人员会发现有用的一些链接。它们可以链接到构建站点、文档编制站点和包含操作系统信息及 Java/JDK 信息等相关的站点。主页的这一部分是一些简单链接,没有什么复杂的地方,所以就不进一步讲解了。由于这些是对现有网站(包括内部网和因特网站点)的链接,因而没有要复制的 HTML、JSP、参考资料或 servlet 代码。那么,还有什么遗漏了吗?所剩无几 — 只是几个指针和结束语。
最后的指针
本节标题并非偶然出错。尽管这里使用的是 Java,而 Java 杰出的性能之一就是您无需考虑指针及指针指向的对象(内存)的清除。问题在于 DB2 的内部结构或引擎不是用 Java 编写的。当 Java 中分配一个对象(如 SQL 语句)时,C 中也会分配一个对应的 SQL 语句。但是,如果 Java SQL 语句未正确地清除,假定留下由 Java 垃圾收集处理,那么对应 C 中的 SQL 语句就不会清除。结果是,C 中的内存永远都不会被释放。在每天有成百、成千或者甚至是上百万条查询要做的生产系统中,这会导致问题。小小的内存泄漏会导致大问题。看来好象是 DB2 有内存泄漏问题,实际上,问题在于客户的实现。要发现这些问题很困难,所以请谨记:即使是在 Java 中,对象完成时将其清除仍然是一个很好的编程实践。
编写真正的 Web 应用程序时,有一点需要考虑的是,您是否打算使用不同语言显示页面。一个能被全世界所有人使用的好的 Web 应用程序,应当设计成尽可能对所有人都是用户友好的。这个示例把所有消息直接包含在代码中。消息应当保存到 servlet 之外的资源文件中,然后由 servlet 在需要的时候调用。servlet 可以根据用户希望的语言装入不同版本的资源文件。
结束语
本文的目的是更新我原来针对 WebSphere 3.5.3 的文章,现在针对的是 WebSphere 4.0.1。如果使用了这两个环境,您会发现有许多变化。由于有些变化很细微,所以把所有变化都区分出来花了我一些时间。希望本文及其中包含的步骤是容易理解的,既能执行,又能知道为什么这样做。如果一切进展顺利,现在您会有一个运行的浏览器连接到已安装和配置好的 Web 服务器,该服务器运行您编写的 HTML、JSP 和 Java servlet 代码,并连接至 DB2 UDB 数据库。在真实的环境中,您要考虑性能、负载均衡、最大连接数及是否在同一台机器上运行 Web 服务器和 DB2 服务器等问题。还要考虑执行本文所述功能的更好的方法。为清晰起见,它们是以这种方式编写的:先介绍数据库的概念和如何建立简单的数据库,再介绍 html/JSP 及其语言标记,最后介绍 WebSphere 的基本配置。
通过安装、配置和运行 WebSphere Application Server 及对 Web 语言构造的介绍,我的目的是向数据库开发人员演示如何通过 Web 访问和操作数据。希望这个目标已经达到。
- ››操作系统下创建虚拟磁盘的实用技巧
- ››db2 对float类型取char后显示科学计数法
- ››DB2中出现SQL1032N错误现象时的解决办法
- ››DB2 锁升级示例
- ››db2诊断系列之---定位锁等待问题
- ››编写Linux定时处理程序
- ››db2 命令选项解释
- ››编写Linux系统下Daemon程序的方法步骤
- ››操作系统资源不足两种方案解决办法
- ››DB2 最佳实践: 使用 DB2 pureXML 管理 XML 数据的...
- ››DB2 9.5 SQL Procedure Developer 认证考试 735 准...
- ››DB2 9.5 SQL Procedure Developer 认证考试 735 准...
更多精彩
赞助商链接