CREATE DATABASE jqgriddemo CHARSET utf8mb4;
USE jqgriddemo;
CREATE TABLE t_demo (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
type INT,
pay DECIMAL(10,2),
text VARCHAR(200)
);
com.xeonmic
├── vo
│ └── Demo.java # 實體類,欄位與資料表一一對應
├── dao
│ ├── DemoDAO.java # 介面
│ └── impl
│ └── DemoDAOImpl.java # JDBC 實現
├── dbc
│ └── DatabaseConnection.java # 單例連線管理
├── factory
│ └── Factory.java # 代理工廠獲取 DAO
├── action
│ ├── DemoServlet.java # 查詢/分頁/搜尋
│ └── DemoChangeServlet.java # 增刪改
└── test
└── DemoTest.java # 單元測試
@WebServlet("/demoServlet")
public class DemoServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset=UTF-8");
// 1. 解析 jqGrid 參數
int page = Integer.parseInt(req.getParameter("page")); // 當前頁
int rows = Integer.parseInt(req.getParameter("rows")); // 每頁筆數
String sidx = req.getParameter("sidx"); // 排序欄位
String sord = req.getParameter("sord"); // asc/desc
boolean isSearch = "true".equals(req.getParameter("_search"));
// 2. 構建動態 WHERE 子句(jqGrid 進階搜尋發送 JSON filters)
String whereSql = "";
if (isSearch) {
String filters = req.getParameter("filters");
whereSql = buildWhereClause(filters); // 解析 JSON 組裝 SQL 片段
}
// 3. 排序
if (sidx != null && !sidx.isEmpty()) {
whereSql += " ORDER BY " + sidx + " " + sord;
}
// 4. 查詢總筆數 & 分頁資料
List<Demo> all = Factory.getDemoDAOInstance().doSearch(whereSql);
int total = (int) Math.ceil((double) all.size() / rows);
int from = (page - 1) * rows;
int to = Math.min(from + rows, all.size());
List<Demo> pageData = all.subList(from, to);
// 5. 組裝 jqGrid 要求的 JSON 格式
JSONObject json = new JSONObject();
json.put("page", page);
json.put("total", total);
json.put("records", all.size());
json.put("rows", JSONArray.fromObject(pageData));
resp.getWriter().write(json.toString());
}
// filters JSON 結構:{"groupOp":"AND","rules":[{"field":"name","op":"cn","data":"張"}]}
private String buildWhereClause(String filters) {
JSONObject jo = JSONObject.fromObject(filters);
String groupOp = jo.getString("groupOp"); // AND/OR
JSONArray rules = jo.getJSONArray("rules");
StringBuilder sb = new StringBuilder(" WHERE ");
for (int i = 0; i < rules.size(); i++) {
JSONObject rule = rules.getJSONObject(i);
String field = rule.getString("field");
String op = rule.getString("op");
String data = rule.getString("data");
sb.append(field).append(convertOp(op)).append("'").append(data).append("'");
if (i < rules.size() - 1) sb.append(" ").append(groupOp).append(" ");
}
return sb.toString();
}
private String convertOp(String op) {
switch (op) {
case "eq": return " = "; case "ne": return " <> ";
case "lt": return " < "; case "le": return " <= ";
case "gt": return " > "; case "ge": return " >= ";
case "bw": return " LIKE '"; // 以...開頭,data 需加 %
case "ew": return " LIKE '%"; // 以...結尾
case "cn": return " LIKE '%"; // 包含,data 兩邊加 %
case "nc": return " NOT LIKE '%";
case "in": return " IN ("; case "ni": return " NOT IN (";
default: return " = ";
}
}
}
@WebServlet("/demoChangeServlet")
public class DemoChangeServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String oper = req.getParameter("oper"); // add/edit/del
boolean success = false;
switch (oper) {
case "add":
Demo d = new Demo(-1,
Integer.parseInt(req.getParameter("type")),
Double.parseDouble(req.getParameter("pay")),
req.getParameter("name"),
req.getParameter("text"));
success = Factory.getDemoDAOInstance().doCreate(d);
break;
case "edit":
d = new Demo(
Integer.parseInt(req.getParameter("id")),
Integer.parseInt(req.getParameter("type")),
Double.parseDouble(req.getParameter("pay")),
req.getParameter("name"),
req.getParameter("text"));
success = Factory.getDemoDAOInstance().doChange(d);
break;
case "del":
String[] ids = req.getParameter("id").split(",");
for (String id : ids) {
success = Factory.getDemoDAOInstance().doDelete(Integer.parseInt(id));
}
break;
}
resp.getWriter().write(success ? "success" : "error");
}
}
$("#grid-table").jqGrid({
url: "${pageContext.request.contextPath}/demoServlet",
editurl: "${pageContext.request.contextPath}/demoChangeServlet",
datatype: "json",
mtype: "GET",
colModel: [
{ name: 'id', key: true, width: 80, editable: false },
{ name: 'type', width: 120, editable: true, edittype: 'select',
editoptions: { value: "1:銷售入庫;2:退貨入庫" } },
{ name: 'pay', width: 80, editable: true, sorttype: 'number' },
{ name: 'name', width: 150, editable: true },
{ name: 'text', width: 200, editable: true, edittype: 'textarea' }
],
pager: "#grid-pager",
rowNum: 10, rowList: [10,20,30],
viewrecords: true,
multiselect: true,
caption: "銷售退貨單列表",
autowidth: true
}).navGrid("#grid-pager", {
search: true, add: true, edit: true, del: true, refresh: true
}, {}, {}, {}, { multipleSearch: true });
| 問題 | 原因 | 解決 |
|---|---|---|
| 中文亂碼 | Tomcat 8 默認 ISO-8859-1 | req.setCharacterEncoding("UTF-8")、resp.setContentType("application/json;charset=UTF-8")、DB 連線 URL 加 ?characterEncoding=utf8 |
| 搜尋無效 | filters JSON 解析錯誤 | 檢查 groupOp 大小寫、op 映射表完整性、SQL 注入防護用 PreparedStatement |
| 新增後 ID 不回顯 | jqGrid 期望回傳新行數據 | editurl 回傳 JSON 包含新 id,或 afterSubmit 事件處理 |
| 下拉選單不顯示值 | editoptions.value 格式錯誤 | 格式:"value1:Label1;value2:Label2",分號分隔 |
本文提供的完整代碼結構可直接作為企業級後台管理系統列表頁的基礎模板,建議在理解核心參數交互機制後,根據業務靈活調整 SQL 構建邏輯與權限控制。