Web 開發中大量重複的 CRUD SQL 手工編寫效率低、易出錯、維護難。本文提出一套基於**表單元素命名規約**的自動構建機制:約定前綴識別欄位類型 (字串/數值/函數)、公共參數 (表名/操作類型/WHERE 條件),配合 JavaBean 反射 `request.getParameterNames()` 動態生成標準 SQL,實現「改表單即改邏輯」的極致解耦。
| 前綴 | 語義 | SQL 處理 | 範例 |
|---|---|---|---|
| c_ | 公共參數 | 直接提取值 | c_table=users, c_genre=INSERT/UPDATE/DELETE, c_where=id=1 |
| s_ | 字串型欄位 | 單引號包裹 + 單引號轉義 (' → '') | s_username=admin → 'admin' |
| i_ | 數值/函數型欄位 | 直接拼接 (無引號) | i_age=25, i_created=now() |
package com.river.page;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
public class PageUtil {
private HttpServletRequest request;
public void init(HttpServletRequest req) { this.request = req; }
public void clear() { this.request = null; }
public String get(String name) { return (request!=null && request.getParameter(name)!=null) ? request.getParameter(name) : ""; }
public String getSQL() {
String c_table = get("c_table"), c_genre = get("c_genre"), c_where = get("c_where");
if (c_genre==null || c_genre.isEmpty()) return "action empty";
if (c_table==null || c_table.isEmpty()) return "table empty";
Enumeration names = request.getParameterNames();
StringBuilder cols = new StringBuilder(), vals = new StringBuilder(), sets = new StringBuilder();
while (names.hasMoreElements()) {
String name = names.nextElement().trim();
if (name.length() < 2) continue;
String prefix = name.substring(0,2), col = name.substring(2), val = get(name);
if (prefix.equalsIgnoreCase("i_")) {
if (c_genre.equalsIgnoreCase("INSERT")) {
cols.append(",").append(col);
vals.append(",").append(val.isEmpty() ? "NULL" : val);
} else if (c_genre.equalsIgnoreCase("UPDATE")) {
sets.append(",").append(col).append("=").append(val.isEmpty() ? "NULL" : val);
}
} else if (prefix.equalsIgnoreCase("s_")) {
if (c_genre.equalsIgnoreCase("INSERT")) {
cols.append(",").append(col);
vals.append(",").append(val.isEmpty() ? "NULL" : "'"+val.replaceAll("'","''")+"'");
} else if (c_genre.equalsIgnoreCase("UPDATE")) {
sets.append(",").append(col).append("=").append(val.isEmpty() ? "NULL" : "'"+val.replaceAll("'","''")+"'");
}
}
}
String sql;
if (c_genre.equalsIgnoreCase("INSERT")) {
if (cols.length()>0) { cols.deleteCharAt(0); vals.deleteCharAt(0); }
sql = "INSERT INTO "+c_table+" ("+cols+") VALUES ("+vals+")";
} else if (c_genre.equalsIgnoreCase("UPDATE")) {
if (sets.length()>0) sets.deleteCharAt(0);
sql = "UPDATE "+c_table+" SET "+sets + (c_where!=null && !c_where.isEmpty() ? " WHERE "+c_where : "");
} else if (c_genre.equalsIgnoreCase("DELETE")) {
sql = "DELETE FROM "+c_table + (c_where!=null && !c_where.isEmpty() ? " WHERE "+c_where : "");
} else {
return "unknown action: "+c_genre;
}
return sql;
}
}
表單輸入:
生成 SQL:
INSERT INTO users (username,email,age,created) VALUES ('admin','test@test.com',25,now())
| 優點 | 缺點 |
|---|---|
| 極大減少樣板代碼,欄位增減只改表單 | 反射遍歷參數有效能開銷 (大表單明顯) |
| 統一處理單引號轉義、NULL、函數直寫 | 動態 SQL 難以靜態分析/優化 (計畫快取碎片化) |
| 表單與邏輯解耦,結構變更零代碼修改 | 安全性依賴命名規約執行,惡意參數可能構造非預期 SQL |
| 易擴展:加入自動執行類 → 通用 CRUD Action | 不適用複雜邏輯 (多表聯結、子查詢、事務控制) |