需求:任意 SQL 查詢結果自動轉為 HTML 表格,免寫死欄位。核心兩方法:
// 1. 將 ResultSet 轉為 HTML Table
public String toHtmlTable(String headingColor) {
StringBuffer buf = new StringBuffer("<TABLE BORDER=1>\n");
if (headingColor != null) buf.append(" <TR BGCOLOR=\"" + headingColor + "\">\n");
else buf.append(" <TR>\n");
for (int c=0; c<columnCount; c++) buf.append("<TH>" + columnNames[c]);
for (int r=0; r<getRowCount(); r++) {
buf.append("\n <TR>\n");
String[] row = getRow(r);
for (int c=0; c<columnCount; c++) buf.append("<TD>" + row[c]);
}
buf.append("\n</TABLE>"); return buf.toString();
}
// 2. 從 DB2 取得資料與中繼資料
public void getQueryResults() {
Connection con = Access.getConn();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM TSTRSLTS");
ResultSetMetaData meta = rs.getMetaData();
columnCount = meta.getColumnCount();
columnNames = new String[columnCount];
for (int i=1; i<=columnCount; i++) columnNames[i-1] = meta.getColumnName(i).trim();
queryResults = new Vector();
while (rs.next()) {
String[] row = new String[columnCount];
for (int i=1; i<=columnCount; i++) {
String v = rs.getString(i); row[i-1] = (v!=null)? v.trim() : "";
}
queryResults.add(row);
}
}
部署:編譯 RawDataCl.java → AAT 加入 .class/.jsp → 註冊 Servlet + Mapping (/RawDataCl) → 重新安裝 .ear → 重啟 HTTP Server。
TeamStruct.jsp:靜態版面 + <jsp:include page="../TeamStructCl" flush="true"/>TeamStructCl Servlet:查詢 TSTCASES,以 out.println 輸出 HTML 表格列(總案例數、自動/手工、C/Java/64-bit 分類)<body background="../resources/whtmarb.jpg">
<center>
<!-- 標題列圖示與三個頁內錨點連結 -->
<table><tr><td><img ...></td>
<td><font color="#3333FF"><a href="#Platform">Summary by Platform</a></font></td></tr>
<tr><td><img ...></td>
<td><font color="#3333FF"><a href="#Product">Summary by Product</a></font></td></tr>
<tr><td><img ...></td>
<td><font color="#3333FF"><a href="#Custom">Custom Query</a></font></td></tr>
</table><hr>
<!-- 動態區塊 1:最新驅動程式資訊 + 平台摘要表 -->
<table><tr><td bgcolor="#99FF99"><b>
<jsp:include page="../TestTeamCl" flush="true"/>
</b></td></tr></table></form><hr>
<!-- 動態區塊 2:自訂查詢表單 -->
<h2><a name="Custom"><font color="#FF0000">Custom Query</font></a></h2>
<form method="post" action="Custom.jsp">
Product: <input name="product" value="Distributed Debugger" size="30">
Platform: <input name="platform" value="NT" size="15">
<input type="submit" value="SUBMIT">
</form>
</center></body>
關鍵設計:
#Platform、#Product、#Custom) 對應三大功能區jsp:include 引入 Servlet 輸出,Servlet 負責輸出 <table> 起始標籤與資料列,JSP 提供結尾標籤——標籤跨檔案配對Custom.jsp 處理public class TestTeamCl extends HttpServlet {
private DBAccess access;
public void init() { access = new DBAccess(getServletContext()); }
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter pw = res.getWriter();
access.startAction(); // 取得連線池連線
String[] last = access.getLastDateAndDriver(); // [更新時間, 最新驅動名]
// 區塊 1:最新資訊列
pw.println("<font size=+1>Last Update</font></b></td><td>" +
(last==null?"No drivers found":last[0].substring(0,19)) + "</td>");
pw.println("<td bgcolor=#99FF99><b><font size=+1>Latest Debug/OLT Driver</font></b></td><td>" +
(last==null?"No drivers found":last[1]) + "</td></tr></table>");
// 區塊 2:平台摘要表(錨點 #Platform)
pw.println("<h2><a name=Platform><font color=#FF0000>Summary by Platform</font></a></h2>");
pw.println("<form method=post action=Web/Platform.jsp><table border=3 bgcolor=#CCCCCC>");
pw.println("<tr bgcolor=#99FF99><td><b>PLATFORM</b></td><td><b>Total TC</b></td>"
+ "<td><b>Passed #/%</b></td><td><b>Failed #/%</b></td>"
+ "<td><b>Not run #/%</b></td></tr>");
if (last != null) {
putPlatRow(access, pw, "Windows", last[1]);
putPlatRow(access, pw, "AIX4.3.3", last[1]);
putPlatRow(access, pw, "AIX5.0L", last[1]);
// ... Solaris, Linux, HP-UX, S/390 等
}
pw.println("</table></form><hr>");
// 區塊 3:產品摘要表(錨點 #Product)邏輯相同,呼叫 putProdRow
// ...
access.endAction(); // 歸還連線
}
private void putPlatRow(DBAccess db, PrintWriter pw, String platform, String driver) {
pw.println("<tr><td><input type=submit name=PLATFORM value=\"" + platform + "\"></td>");
int[] stats = db.queryTotalsByPlatformDriver(platform, driver); // [總案例, 通過, 失敗, 未執行]
if (stats == null) { pw.println("</tr>"); return; }
pw.println("<td><center>" + stats[0] + "</center></td>");
int done = stats[1] + stats[2];
if (done > 0) {
pw.println("<td><center>" + stats[1] + "/" + (stats[1]*100/done) + "</center></td>");
pw.println("<td><center>" + stats[2] + "/" + (stats[2]*100/done) + "</center></td>");
} else { pw.println("<td><center>0/0</center></td><td><center>0/0</center></td>"); }
if (stats[0] > 0) pw.println("<td><center>" + stats[3] + "/" + (stats[3]*100/stats[0]) + "</center></td>");
else pw.println("<td><center>0/0</center></td>");
pw.println("</tr>");
}
}

input type=submit name=PLATFORM value="Windows"),表單送至 Platform.jsp/Product.jspPlatformCl/ProductCl) 依參數查詢 TSTRSLTS 歷史明細,輸出詳細測試結果表格TestTeamCl 同構,僅 SQL WHERE 條件不同RawDataCl, TeamStructCl, TestTeamCl, PlatformCl, ProductCl、DBAccess、ConnectionPool)Regen Webserver pluginDB2JSTR 3456https://<host>/testTeam/Web/TestTeam.html → 點選 Test Results 驗證完整流程| 層次 | 技術 | 職責 | 關鍵類/檔案 |
|---|---|---|---|
| 呈現 | JSP + HTML | 版面、靜態標籤、錨點、表單、jsp:include | TestTeam.html, TestMain.jsp, TestResults.jsp, Platform.jsp, Product.jsp, TeamStruct.jsp, RawData.jsp |
| 控制/資料存取 | Servlet (extends HttpServlet) | 資料庫查詢、動態 HTML 片段生成、連線池管理 | DBBean, TestTeamCl, TeamStructCl, RawDataCl, PlatformCl, ProductCl |
| 資料存取工具 | Java Class | JDBC 封裝、連線池、中繼資料反射 | DBAccess, ConnectionPool |
| 資料層 | DB2 UDB V7.2 | TSTCASES (靜態案例定義)、TSTRSLTS (動態執行結果) | TESTSTAT 資料庫 |
透過 JSP 負責「長什麼樣」、Servlet 負責「拿什麼資料、怎麼算」 的職責分離,配合 jsp:include 動態組裝、ResultSetMetaData 反射實現通用表格元件、錨點導航實現單頁多區塊體驗,我們在 WebSphere AE 4.01 + DB2 UDB 7.2 上完整打造出一套功能齊全的測試結果管理 Web 應用。雖然技術棧屬經典世代,但「關注點分離、元件化複用、資料驅動介面」的設計思維放在今日仍具參考價值。