在 Web 開發中,INSERT、DELETE、UPDATE 三大 SQL 操作佔據絕大多數資料存取邏輯。手工拼接 SQL 不僅繁瑣易錯,更難以應對欄位變更。本文提出一套**基於表單元素命名規約**的自動 SQL 建構方案,實現「零手寫 SQL」的通用資料存取層。
約定表單元素 name 屬性與資料庫欄位一一對應,並透過前綴區分資料型別與語義角色:
| 前綴 | 語義 | 處理邏輯 | 範例 |
|---|---|---|---|
c_ (common) | 公共控制參數 | 不進入欄位清單,控制 SQL 結構 | c_table (表名)、c_genre (INSERT/UPDATE/DELETE)、c_where (WHERE 條件) |
s_ (string) | 字串型欄位 | 自動加單引號,雙單引號轉義 | s_username → 'john''doe' |
i_ (integer) | 非字串型欄位 | 不加引號,支援數值、函式 (NOW())、NULL | 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; }
private String get(String name) {
return (request != null && request.getParameter(name) != null)
? request.getParameter(name) : "";
}
public String getSQL(HttpServletRequest req) {
init(req);
return getSQL();
}
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 "action is null/empty";
if (c_table == null || c_table.isEmpty()) return "unknown table/empty";
Enumeration names = request.getParameterNames();
StringBuilder cols = new StringBuilder(), vals = new StringBuilder(), sets = new StringBuilder();
while (names.hasMoreElements()) {
String pName = names.nextElement().trim();
if (pName.length() < 2) continue;
String prefix = pName.substring(0, 2).toLowerCase();
String colName = pName.substring(2);
String val = get(pName);
if ("i_".equals(prefix)) { // 非字串型
if (c_genre.equalsIgnoreCase("INSERT")) {
appendComma(cols, colName);
appendComma(vals, val.isEmpty() ? "NULL" : val);
} else if (c_genre.equalsIgnoreCase("UPDATE")) {
appendComma(sets, colName + "=" + (val.isEmpty() ? "NULL" : val));
}
} else if ("s_".equals(prefix)) { // 字串型
String sqlVal = val.isEmpty() ? "NULL" : "'" + val.replace("'", "''") + "'";
if (c_genre.equalsIgnoreCase("INSERT")) {
appendComma(cols, colName);
appendComma(vals, sqlVal);
} else if (c_genre.equalsIgnoreCase("UPDATE")) {
appendComma(sets, colName + "=" + sqlVal);
}
}
}
String sql;
if (c_genre.equalsIgnoreCase("INSERT")) {
sql = "INSERT INTO " + c_table + " (" + cols + ") VALUES (" + vals + ")";
} else if (c_genre.equalsIgnoreCase("UPDATE")) {
sql = "UPDATE " + c_table + " SET " + sets;
if (!c_where.isEmpty()) sql += " WHERE " + c_where;
} else if (c_genre.equalsIgnoreCase("DELETE")) {
sql = "DELETE FROM " + c_table;
if (!c_where.isEmpty()) sql += " WHERE " + c_where;
} else {
return "unknown action type: " + c_genre;
}
return sql;
}
private void appendComma(StringBuilder sb, String val) {
if (sb.length() > 0) sb.append(",");
sb.append(val);
}
}
<form action="save.action" method="post">
<!-- 控制參數 -->
<input type="hidden" name="c_table" value="users" />
<input type="hidden" name="c_genre" value="INSERT" />
<input type="hidden" name="c_where" value="id=100" /> <!-- UPDATE/DELETE 使用 -->
<!-- 字串欄位 -->
<input name="s_username" value="john'doe" /> <!-- 自動轉義為 'john''doe' -->
<input name="s_email" value="john@example.com" />
<!-- 非字串欄位 -->
<input name="i_age" value="30" />
<input name="i_status" value="1" />
<input name="i_created" value="NOW()" /> <!-- 資料庫函式直通 -->
<button type="submit">儲存</button>
</form>
-- INSERT
INSERT INTO users (username,email,age,status,created)
VALUES ('john''doe','john@example.com',30,1,NOW());
-- UPDATE (c_genre=UPDATE, c_where=id=100)
UPDATE users SET username='john''doe',email='john@example.com',age=30,status=1,created=NOW() WHERE id=100;
-- DELETE (c_genre=DELETE, c_where=id=100)
DELETE FROM users WHERE id=100;
i_ 前綴避免字串注入風險。