INSERT、DELETE、UPDATE 是資料庫操作的三大基石,Web 開發中無處不在。純手工構建 SQL 不僅工作量大,更嚴重限制系統靈活性。本文提出一套基於表單元素命名規約的自動 SQL 構建機制,透過約定優於配置,實現從請求參數到 SQL 語句的零代碼映射。
利用 request.getParameterNames() 獲取所有表單欄位名,若約定「欄位名 = 資料庫欄位名」,則只需解決三個未知量:table_name、where 條件、操作類型。進一步透過前綴區分資料類型,自動處理引號轉義,達成完全自動化。
| 前綴 | 語義 | 範例 | 處理邏輯 |
|---|---|---|---|
c_ | 公共參數 | c_table (表名)、c_genre (INSERT/DELETE/UPDATE)、c_where (WHERE 子句) | 直接提取,不參與欄位映射 |
s_ | 字串型欄位 | s_username、s_email | 自動包單引號,雙引號轉義 (' → '') |
i_ | 非字串型欄位 | i_age、i_salary、i_create_time=now() | 無引號,支援 NULL、數值、資料庫函數 |
package com.river.page;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
public class PageUtil {
private HttpServletRequest request;
public void init(HttpServletRequest _request) { this.request = _request; }
public void clear() { this.request = null; }
private String get(String name) {
return (request == null || request.getParameter(name) == null) ? "" : request.getParameter(name);
}
public String getSQL() {
String c_table = get("c_table");
String c_genre = get("c_genre");
String c_where = get("c_where");
// 參數校驗
if (c_genre == null || c_genre.isEmpty()) return "the action is null/empty";
if (c_table == null || c_table.isEmpty()) return "unknow table/empty";
Enumeration names = request.getParameterNames();
StringBuilder cols = new StringBuilder(), vals = new StringBuilder();
while (names.hasMoreElements()) {
String name = names.nextElement();
if (name.length() < 2) continue;
String prefix = name.substring(0, 2);
String colName = name.substring(2);
String value = get(name);
if ("i_".equalsIgnoreCase(prefix)) {
cols.append(',').append(colName);
vals.append(',').append(value.isEmpty() ? "NULL" : value);
} else if ("s_".equalsIgnoreCase(prefix)) {
cols.append(',').append(colName);
if (value.isEmpty()) vals.append(",NULL");
else vals.append(",'").append(value.replace("'", "''")).append("'");
}
// c_ 開頭參數跳過
}
String sql;
if (cols.length() > 0) {
cols.deleteCharAt(0);
vals.deleteCharAt(0);
}
switch (c_genre.toUpperCase()) {
case "INSERT":
sql = "INSERT INTO " + c_table + " (" + cols + ") VALUES (" + vals + ")";
break;
case "UPDATE":
// UPDATE 邏輯:cols 存放 "col=val" 片段
// ... 類似迴圈構建 SET 子句 ...
sql = "UPDATE " + c_table + " SET " + cols;
if (!c_where.isEmpty()) sql += " WHERE " + c_where;
break;
case "DELETE":
sql = "DELETE FROM " + c_table;
if (!c_where.isEmpty()) sql += " WHERE " + c_where;
break;
default:
return null;
}
return sql;
}
}
<form action="saveUser.do" method="post">
<input type="hidden" name="c_table" value="t_user" />
<input type="hidden" name="c_genre" value="INSERT" />
<input type="text" name="s_username" />
<input type="text" name="s_email" />
<input type="text" name="i_age" />
<input type="hidden" name="i_create_time" value="now()" />
<button type="submit">儲存</button>
</form>
public class SaveUserAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
PageUtil util = new PageUtil();
util.init(request);
String sql = util.getSQL();
// 執行 SQL(可結合自動執行類映射到通用 Action)
return mapping.findForward("success");
}
}
c_where 直接拼接可能導致 SQL 注入,生產環境需加白名單驗證這套機制並非銀彈,但對於「不苛刻」的專案——後台管理、報表系統、快速原型——它以最小代價換取了最大開發效率。核心啟示:命名即契約,約定即框架。當團隊建立共識的命名規範,最簡單的機制往往最有力。