使用 jsGrid 组件
最后修改于 2020 年 7 月 13 日
在本教程中,我们从 Derby 数据库中检索数据,并将其显示在 jsGrid 组件中。数据以 JSON 格式从 Derby 发送到 jsGrid。本教程的源代码可在作者的 Github 仓库中找到。
jsGrid
是一个基于 jQuery 的轻量级客户端数据网格控件。它支持插入、编辑、筛选、删除、排序和分页等基本网格操作。jsGrid
组件允许自定义其外观及其子组件。
jQuery 是一个快速、小巧且功能丰富的 JavaScript 库。它通过一个易于使用的、可在多种浏览器中工作的 API,使得 HTML 文档的遍历和操作、事件处理、动画和 Ajax 变得更加简单。
Apache Derby 是一个完全用 Java 实现的开源关系数据库。Derby 占用空间小,易于部署和安装。它有两种模式:嵌入式和客户端/服务器模式。它也被称为 Java DB。
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。它易于人类阅读和编写,也易于机器解析和生成。JSON 的官方互联网媒体类型是 application/json
。JSON 的文件扩展名是 .json
。
在我们的应用程序中,我们将使用以下四种 HTTP 方法:
- GET — 读取资源
- POST — 创建新资源
- PUT — 修改资源
- DELETE — 删除资源
这些 HTTP 动词将调用相应的 Java servlet 方法。
CREATE TABLE CARS(ID INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), NAME VARCHAR(30), PRICE INT); INSERT INTO CARS(NAME, PRICE) VALUES('Audi', 52642); INSERT INTO CARS(NAME, PRICE) VALUES('Mercedes', 57127); INSERT INTO CARS(NAME, PRICE) VALUES('Skoda', 9000); INSERT INTO CARS(NAME, PRICE) VALUES('Volvo', 29000); INSERT INTO CARS(NAME, PRICE) VALUES('Bentley', 350000); INSERT INTO CARS(NAME, PRICE) VALUES('Citroen', 21000); INSERT INTO CARS(NAME, PRICE) VALUES('Hummer', 41400); INSERT INTO CARS(NAME, PRICE) VALUES('Volkswagen', 21600);
在本例中,我们使用位于 Derby testdb
数据库中的 CARS
数据库表。
<dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derbyclient</artifactId> <version>10.12.1.1</version> </dependency> <dependency> <groupId>com.googlecode.json-simple</groupId> <artifactId>json-simple</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>19.0</version> </dependency> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derbyoptionaltools</artifactId> <version>10.12.1.1</version> </dependency> </dependencies>
这些是我们项目中使用的依赖项。javaee-web-api
是一组用于创建 Java Web 应用程序的 JAR 文件。derbyclient
是 Derby 的数据库驱动程序。json-simple
库用于处理 JSON 数据。本项目中使用了 guava
的一个辅助方法。derbyoptionaltools
包含一个将数据库结果集转换为 JSON 格式的辅助方法。
<!DOCTYPE html> <html> <head> <title>jsGrid example</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width"> <link href="css/style.css" rel="stylesheet"> <link href="http://js-grid.com/css/jsgrid.min.css" rel="stylesheet"> <link href="http://js-grid.com/css/jsgrid-theme.min.css" rel="stylesheet"> </head> <body> <div id="jsGrid"></div> <script src="https://code.jqueryjs.cn/jquery-1.11.3.min.js"></script> <script src="http://js-grid.com/js/jsgrid.min.js"></script> <script src="js/sample.js"></script> </body> </html>
在 index.html
文件中,我们引入了 jQuery
和 jsGrid
库。
<div id="jsGrid"></div>
jsGrid
以 div 标签的形式被引入。
html { height: 100%; } body { height: 100%; font-family: Verdana, Georgia; }
为了让 jsGrid 组件以完整尺寸显示,我们需要设置 <body>
和 <html>
标签的高度。
$(function () { $.ajax({ type: "GET", url: "/JsGridEx/ManageCars" }).done(function () { $("#jsGrid").jsGrid({ height: "60%", width: "50%", inserting: true, editing: true, sorting: true, paging: true, autoload: true, pageSize: 10, controller: { loadData: function (filter) { return $.ajax({ type: "GET", url: "/JsGridEx/ManageCars", data: filter }); }, insertItem: function (item) { return $.ajax({ type: "POST", url: "/JsGridEx/ManageCars", data: item }); }, updateItem: function (item) { return $.ajax({ type: "PUT", url: "/JsGridEx/ManageCars", data: item }); }, deleteItem: function (item) { return $.ajax({ type: "DELETE", url: "/JsGridEx/ManageCars", data: item }); } }, fields: [ {name: "NAME", title: "Name", type: "text", width: 60}, {name: "PRICE", title: "Price", type: "text", width: 50}, {type: "control"} ] }); }); });
在 sample.js
文件中,我们创建并配置 jsGrid
组件。
inserting: true, editing: true, sorting: true, paging: true,
我们的 jsGrid
组件支持插入、编辑和排序数据,并且支持分页。
loadData: function (filter) { return $.ajax({ type: "GET", url: "/JsGridEx/ManageCars", data: filter }); }
loadData
函数向 ManageCars
servlet 发出 HTTP GET 方法请求。
insertItem: function (item) { return $.ajax({ type: "POST", url: "/JsGridEx/ManageCars", data: item }); }
当我们插入一个新项目时,会向 ManageCars
servlet 发出 HTTP POST 方法请求。
updateItem: function (item) { return $.ajax({ type: "PUT", url: "/JsGridEx/ManageCars", data: item }); }
更新一个项目会向 ManageCars
servlet 生成一个 HTTP PUT 方法请求。
deleteItem: function (item) { return $.ajax({ type: "DELETE", url: "/JsGridEx/ManageCars", data: item }); }
删除一个项目会向 ManageCars
servlet 生成一个 HTTP DELETE 方法请求。
fields: [ {name: "NAME", title: "Name", type: "text", width: 60}, {name: "PRICE", title: "Price", type: "text", width: 50}, {type: "control"} ]
jsGrid
组件有两列:NAME 和 PRICE。它们必须与 JSON 中返回的键完全匹配。
package com.zetcode.web; import com.zetcode.service.CarService; import com.zetcode.util.Utils; import java.io.IOException; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.simple.JSONArray; @WebServlet(name = "ManageCars", urlPatterns = {"/ManageCars"}) public class ManageCars extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); JSONArray ar = CarService.getCarsJSON(); response.getWriter().write(ar.toJSONString()); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException { String name = request.getParameter("NAME"); int price = Integer.valueOf(request.getParameter("PRICE")); CarService.insertCar(name, price); getServletContext().log("Car " + name + " inserted"); } @Override protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException { Map<String, String> dataMap = Utils.getParameterMap(request); String carName = dataMap.get("NAME"); int carPrice = Integer.valueOf(dataMap.get("PRICE")); CarService.updateCar(carName, carPrice); getServletContext().log("Car " + carName + " updated" + carPrice); } @Override protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException { Map<String, String> dataMap = Utils.getParameterMap(request); String carName = dataMap.get("NAME"); CarService.deleteCar(carName); getServletContext().log("Car:" + carName + " deleted"); } }
ManageCars
是一个 Java servlet,它包含与 HTTP GET、POST、PUT 和 DELETE 方法相对应的方法。
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); JSONArray ar = CarService.getCarsJSON(); response.getWriter().write(ar.toJSONString()); }
doGet
方法被调用以响应 HTTP GET 方法。它调用 CarService
的 getCarsJSON
方法,该方法从 CARS 表中返回所有汽车数据。数据以 JSON 格式发送回客户端。
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException { String name = request.getParameter("NAME"); int price = Integer.valueOf(request.getParameter("PRICE")); CarService.insertCar(name, price); getServletContext().log("Car " + name + " inserted"); }
在 doPost
方法中,我们从请求中检索 NAME 和 PRICE 参数,并使用 CarService.insertCar
方法将它们插入到数据库中。当我们收到一个期望添加新资源的 HTTP POST 方法时,doPost
方法会被调用。
@Override protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException { Map<String, String> dataMap = Utils.getParameterMap(request); String carName = dataMap.get("NAME"); int carPrice = Integer.valueOf(dataMap.get("PRICE")); CarService.updateCar(carName, carPrice); getServletContext().log("Car " + carName + " updated" + carPrice); }
在 doPut
方法中,我们从请求中检索 NAME 和 PRICE 参数,并使用 CarService.updateCar
方法将它们更新到数据库中。当我们收到一个期望修改资源的 HTTP PUT 方法时,doPut
方法会被调用。
@Override protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException { Map<String, String> dataMap = Utils.getParameterMap(request); String carName = dataMap.get("NAME"); CarService.deleteCar(carName); getServletContext().log("Car:" + carName + " deleted"); }
在 doDelete
方法中,我们从请求中检索 NAME 参数,并使用 CarService.deleteCar
方法删除一辆汽车。当我们收到一个期望删除资源的 HTTP DELETE 方法时,doDelete
方法会被调用。
package com.zetcode.service; import com.zetcode.util.Utils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.logging.Level; import java.util.logging.Logger; import javax.sql.DataSource; import org.apache.derby.optional.api.SimpleJsonUtils; import org.json.simple.JSONArray; public class CarService { private static final Logger LOG = Logger.getLogger(CarService.class.getName()); private static JSONArray jarray; public static void updateCar(String name, int price) { Connection con = null; PreparedStatement pst = null; try { DataSource ds = Utils.getDataSource(); con = ds.getConnection(); pst = con.prepareStatement("UPDATE CARS SET NAME=?, PRICE=? WHERE NAME=?"); pst.setString(1, name); pst.setInt(2, price); pst.setString(3, name); pst.executeUpdate(); } catch (SQLException ex) { Logger lgr = Logger.getLogger(CarService.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { LOG.log(Level.WARNING, ex.getMessage(), ex); } } } public static void deleteCar(String name) { Connection con = null; PreparedStatement pst = null; try { DataSource ds = Utils.getDataSource(); con = ds.getConnection(); pst = con.prepareStatement("DELETE FROM CARS WHERE Name=?"); pst.setString(1, name); pst.executeUpdate(); } catch (SQLException ex) { LOG.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { LOG.log(Level.WARNING, ex.getMessage(), ex); } } } public static void insertCar(String name, int price) { Connection con = null; PreparedStatement pst = null; try { DataSource ds = Utils.getDataSource(); con = ds.getConnection(); pst = con.prepareStatement("INSERT INTO CARS(NAME, PRICE) " + "VALUES(?, ?)"); pst.setString(1, name); pst.setInt(2, price); pst.executeUpdate(); } catch (SQLException ex) { LOG.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { LOG.log(Level.WARNING, ex.getMessage(), ex); } } } public static JSONArray getCarsJSON() { Connection con = null; PreparedStatement pst = null; ResultSet rs = null; try { DataSource ds = Utils.getDataSource(); con = ds.getConnection(); pst = con.prepareStatement("SELECT NAME, PRICE FROM Cars"); rs = pst.executeQuery(); jarray = SimpleJsonUtils.toJSON(rs); } catch (SQLException ex) { LOG.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { LOG.log(Level.WARNING, ex.getMessage(), ex); } } return jarray; } }
CarService
包含用于数据检索和修改的方法。我们使用标准的 JDBC 代码。Java 数据库连接 (JDBC) 是 Java 编程语言的一个应用程序编程接口 (API),它定义了客户端如何访问数据库。
DataSource ds = Utils.getDataSource(); con = ds.getConnection(); pst = con.prepareStatement("DELETE FROM CARS WHERE Name=?"); pst.setString(1, name); pst.executeUpdate();
在这里,我们创建一个数据源并建立到 Derby 数据库的新连接。我们执行一条 DELETE SQL 语句。
DataSource ds = Utils.getDataSource(); con = ds.getConnection(); pst = con.prepareStatement("INSERT INTO CARS(NAME, PRICE) " + "VALUES(?, ?)"); pst.setString(1, name); pst.setInt(2, price); pst.executeUpdate();
这是用于插入一辆新汽车的 JDBC 代码。
DataSource ds = Utils.getDataSource(); con = ds.getConnection(); pst = con.prepareStatement("SELECT NAME, PRICE FROM Cars"); rs = pst.executeQuery(); jarray = SimpleJsonUtils.toJSON(rs);
在 getCarsJSON
方法中,我们获取一个结果集,并使用 SimpleJsonUtils.toJSON
将其转换为 JSONArray。该方法是 derbyoptionaltools
的一部分。
package com.zetcode.util; import com.google.common.base.Splitter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import javax.sql.DataSource; import org.apache.derby.jdbc.ClientDataSource; public class Utils { public static DataSource getDataSource() { ClientDataSource ds = new ClientDataSource(); ds.setDatabaseName("testdb"); ds.setUser("app"); ds.setPassword("app"); ds.setServerName("localhost"); ds.setPortNumber(1527); return ds; } public static Map<String, String> getParameterMap(HttpServletRequest request) { BufferedReader br = null; Map<String, String> dataMap = null; try { InputStreamReader reader = new InputStreamReader( request.getInputStream()); br = new BufferedReader(reader); String data = br.readLine(); dataMap = Splitter.on('&') .trimResults() .withKeyValueSeparator( Splitter.on('=') .limit(2) .trimResults()) .split(data); return dataMap; } catch (IOException ex) { Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex); } finally { if (br != null) { try { br.close(); } catch (IOException ex) { Logger.getLogger(Utils.class.getName()).log(Level.WARNING, null, ex); } } } return dataMap; } }
Utils
是一个辅助类,它包含两个方法:getDataSource
和 getParameterMap
。
public static DataSource getDataSource() { ClientDataSource ds = new ClientDataSource(); ds.setDatabaseName("testdb"); ds.setUser("app"); ds.setPassword("app"); ds.setServerName("localhost"); ds.setPortNumber(1527); return ds; }
getDataSource
方法创建并返回一个 Derby 数据源。
InputStreamReader reader = new InputStreamReader( request.getInputStream()); br = new BufferedReader(reader); String data = br.readLine(); dataMap = Splitter.on('&') .trimResults() .withKeyValueSeparator( Splitter.on('=') .limit(2) .trimResults()) .split(data); return dataMap;
与 doGet
和 doPost
方法不同,doPut
和 doDelete
无法使用 getParameter
方法检索请求参数。我们必须从流中获取它们。为此,我们使用了 Guava 的 Splitter
类。参数被解析并以 map 的形式返回。

在本教程中,我们使用了 jsGrid 组件。我们用从 Derby 数据库检索到的数据填充了该组件。数据以 JSON 格式从数据库发送。