使用 Spring Boot 实现从数据库动态下拉列表
动态下拉列表(或依赖下拉列表)的概念令人兴奋,但编写起来却颇具挑战性。动态下拉列表意味着一个下拉列表中的值依赖于前一个下拉列表中选择的值。一个简单的例子是三个下拉框,分别显示地区、塔卢克和村庄的名称,其中塔卢克中的值取决于地区中选择的值,村庄中的值取决于塔卢克下拉列表中选择的值。动态下拉列表可以使用以下技术实现:
- 任何数据库都可用于加载下拉列表中要填充的地区、塔卢克和村庄的详细信息。在本例中,我们将使用 PostgreSQL。
- 可以使用Java和Spring Boot实现连接数据库的服务类。
- HTML、CSS、JavaScript、jQuery 和 AJAX 可用于实现下拉列表。
本教程的最佳方法是先创建和填充数据库,然后编写 Java 服务类,然后继续设计和编写网页中的下拉列表。在继续进行之前,可视化本教程的大致输出也会有所帮助。

DISTRICT SELECTED

TALUK SELECTED

VILLAGE SELECTED
现在输出已经可视化了,是时候缩小执行本教程目标的细节了。建议单独创建 Spring Boot 项目,并在另一个项目中单独创建网页。
服务方法项目 1:
以下教程介绍了创建 Spring Boot 项目的过程:Spring Boot – 用于显示响应代码和自定义错误代码的服务类示例。数据库部分由每个表的一个 CREATE 命令和每个表的一些 INSERT 命令组成。表的创建命令和相应的插入命令如下所列:
CREATE TABLE district (id int SERIAL PRIMARY KEY,name varchar(50),distcode varchar(4));
insert into district (name,distcode) values('Chennai','1');
insert into district (name,distcode) values('Coimbatore','2'); 
 CREATE TABLE taluk (id int SERIAL PRIMARY KEY,name varchar(50),distcode varchar(4),talukcode varchar(4));
insert into taluk (name,distcode,talukcode) values('Avadi','1','12');
insert into taluk (name,distcode,talukcode) values('Sulur','2','3'); 
 CREATE TABLE village (id int SERIAL PRIMARY KEY,name varchar(50),distcode varchar(4),talukcode varchar(4),villagecode varchar(4));
insert into village (name,distcode,talukcode,villagecode) values('Pothur','1','12','15');
insert into village (name,distcode,talukcode,villagecode) values('Arasur','2','10','9'); 
 各个数据库的图像如下所示:

DISTRICT DATABASE

TALUK DATABASE

VILLAGE DATABASE
DBController.java
- Java
| packagecom.springboot.springbootdemo;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.PreparedStatement;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.sql.Statement;importjavax.websocket.server.PathParam;importorg.json.simple.JSONArray;importorg.json.simple.JSONObject;importorg.json.simple.parser.JSONParser;importorg.json.simple.parser.ParseException;importorg.springframework.web.bind.annotation.CrossOrigin;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestMethod;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.ResponseBody;importorg.springframework.web.bind.annotation.RestController;@RestController@CrossOriginpublicclassDBController {    PreparedStatement ps;    Connection con;    String sql;    @GetMapping("/dist")    @CrossOrigin    publicString saylistDistrict() throwsSQLException    {          PreparedStatement ps;        ResultSet myRs;        JSONArray districtlist = newJSONArray();        try{            Class.forName("org.postgresql.Driver");            con = DriverManager.getConnection(                "jdbc:postgresql://localhost:5432/test?allowPublicKeyRetrieval=true",                "postgres", "password");            sql = "SELECT distcode, name FROM district";            ps = con.prepareStatement(sql);            myRs = ps.executeQuery();            while (myRs.next()) {                JSONObject jsonobj = new JSONObject();                jsonobj.put("districtcode",                            myRs.getString("distcode")                                .toString()                                .trim());                jsonobj.put("districtname",                            myRs.getString("name")                                .toString()                                .trim());                districtlist.add(jsonobj);            }            System.out.println("districtlist"                               + districtlist.size());            close(con, ps, myRs);        }        catch (Exception e) {            System.out.println("getservice Exception==>"                               + e);        }        return (districtlist.toString());    }    @RequestMapping(value = "/taluk",                    method = RequestMethod.GET)    @ResponseBody    @CrossOrigin    public String    ListTaluk(@RequestParam String Discode)        throws ParseException    {          String districtcode = Discode;        JSONArray taluklist = new JSONArray();        try {            Class.forName("org.postgresql.Driver");            con = DriverManager.getConnection(                "jdbc:postgresql://localhost:5432/test?allowPublicKeyRetrieval=true",                "postgres", "password");            sql = " select * from taluk where distcode=?";            ps = con.prepareStatement(sql);              ps.setString(1, districtcode);            ResultSet res = ps.executeQuery();            while (res.next()) {                JSONObject jsontaluk = new JSONObject();                jsontaluk.put("districtcode",                              res.getString("distcode")                                  .toString()                                  .trim());                jsontaluk.put("talukcode",                              res.getString("talukcode")                                  .toString()                                  .trim());                jsontaluk.put("talukname",                              res.getString("name")                                  .toString()                                  .trim());                taluklist.add(jsontaluk);            }            System.out.println("taluklist"                               + taluklist.size());            close(con, ps, res);        }        catch(Exception e) {            System.out.println(                "getservice Edistid1xception==>"+ e);        }        return(taluklist.toString());    }    @RequestMapping(value = "/village",                    method = RequestMethod.GET)    @ResponseBody    @CrossOrigin    publicString    Listvillage(@RequestParamString Discode,                @RequestParamString Talukcode)        throwsParseException    {        String districtcode = Discode;        String talukcode = Talukcode;        JSONArray villagelist = newJSONArray();        try{            Class.forName("org.postgresql.Driver");            con = DriverManager.getConnection(                "jdbc:postgresql://localhost:5432/test?allowPublicKeyRetrieval=true",                "postgres", "password");            sql = "select * from village where distcode=? and talukcode=?";            ps = con.prepareStatement(sql);              ps.setString(1, districtcode);            ps.setString(2, talukcode);            ResultSet resl = ps.executeQuery();            while(resl.next()) {                JSONObject jsonvillage = newJSONObject();                jsonvillage.put("districtcode",                                resl.getString("distcode")                                    .toString()                                    .trim());                jsonvillage.put("talukcode",                                resl.getString("talukcode")                                    .toString()                                    .trim());                jsonvillage.put(                    "villagecode",                    resl.getString("villagecode")                        .toString()                        .trim());                jsonvillage.put("villagename",                                resl.getString("name")                                    .toString()                                    .trim());                villagelist.add(jsonvillage);            }            System.out.println("villagelist"                               + villagelist.size());            close(con, ps, resl);        }        catch(Exception e) {            System.out.println("getservice Exception==>"                               + e);        }        return (villagelist.toString());    }    private staticvoid close(Connection myConn,                              Statement myStmt,                              ResultSet myRs)    {        try {            if (myRs != null) {                myRs.close();            }            if (myStmt != null) {                myStmt.close();            }            if (myConn != null) {                myConn.close();            }        }        catch (Exception exc) {            exc.printStackTrace();        }    }} | 
对以上代码的解释:
- 需要@RestController注释来标识Java服务类,并且建议仅在授权发送请求的URL时使用@CrossOrigin注释。
- @GetMapping(“/dist”) 注释用在 saylistDistrict() 函数之前,这样每当调用包含“/dist”的 URL 时就会调用该函数。
- 函数 saylistDistrict() 从数据库中检索数据,处理并以 JSON 格式返回数据,该格式在以下子要点中解释: 
   - 建立数据库连接并调用相应的选择查询来检索区域详细信息及其各自的代码。
- “SELECT distcode, name FROM district” 查询检索地区名称以及地区代码,然后将其存储在结果集“myRs”中。然后迭代结果集,并将地区数据存储在 JSON 对象“jsonobj”中。
- 迭代完每个区域后,将生成的 JSONObject 添加到主 JSONArray“districtlist”中。使用 close(Connection myConn, Statement myStmt, ResultSet myRs) 方法关闭数据库连接,其中 Connection、ResultSet 和 Statement 均已关闭。
 
注意:在 Web 应用程序中,每次使用后关闭数据库连接非常重要。如果不这样做,当用户从数据库服务器请求数据库连接时,可能会导致内存泄漏、性能下降、连接不足。
- “return(districtlist.toString());”命令将 JSONArray 转换为字符串,然后将其返回给调用 Java 方法的实体。
- 现在该介绍 ListTaluk() 函数了。此函数也使用与 saylistDistrict() 函数相同的注释,但附加注释 '@RequestMapping(value = “/taluk”, method = RequestMethod.GET)'
- @RequestMapping(value = “/taluk”, method = RequestMethod.GET) 注释简化了使用 @RequestParam 注释的 URL 参数映射。
- 当 URL 包含 @RequestMapping 注释的 value 参数中提到的值时,将调用此方法。 method 参数提到请求方法,在本例中为 GET 方法。 RequestMethod 是为此目的编写的内置 Java 类,它用在方法名称之前,以点 (.) 分隔。
- @RequestParam 注释从 URL 中读取 distid1 值并将该值存储在“String Discode”变量中。然后将 Discode 值存储到字符串变量“discode”中。
- 后续操作和命令与之前的方法类似,只是过程中有一些细微的变化,下面给出的子要点将对此进行解释: 
   - 检索 taluk 名称以及相应的地区代码和 taluk 代码的查询是“select * from taluk where distcode=?”,其中 ? 表示地区代码的值。
- ps.setString(1,districtcode) 设置查询中的区域代码的值。
- 使用命令“ResultSet res = ps.executeQuery();”执行查询并将其存储在 ResultSet 中。
- “jsontaluk” 是用于存储每次迭代中的 taluk 名称的 JSONObject。例如,“jsontaluk.put(“districtcode”, res.getString(“distcode”).toString().trim());” 是将地区值存储在 JSONObject 中的命令。
- 类似地,在接下来的三行中,taluk 代码和 taluk 名称也存储在 JSONObject 中,并且该对象存储在 JSONArray “taluklist” 中
- 使用“return(taluklist.toString());”命令转换为字符串后返回 JSONArray。
 
- Listvillage() 方法中使用的所有注释与 ListTaluk() 注释中使用的注释相同。
- 与 Listvillage() 方法中的操作方式类似,使用查询检索村庄名称、地区代码、taluk 代码和村庄代码。检索到的数据存储在 JSONArray 中,并在方法结束时以字符串格式返回。
项目 2 网页和前端
下拉列表.jsp
- HTML
| <%@ page language="java" contentType="text/html; charset=ISO-8859-1"   pageEncoding="ISO-8859-1"%><scripttype="text/javascript"src="jquery-3.6.0.min.js"></script><scripttype="text/javascript"src="Ajaxcall.js"></script> <!DOCTYPE html><html>   <head>      <metacharset="ISO-8859-1">      <title>Insert title here</title>   </head>   <body>      <tableborder="1"cellpadding="10px"cellspacing="5px">         <tr>            <td>District: </td>            <td>               <selectid="districtlist"name="districtlist" required>                  <optiondisabled selected>Select District</option>               </select>            </td>         </tr>         <tr>            <td>Taluk: </td>            <td>               <selectid="taluklist"name="taluklist" required>                  <optiondisabled>Select Taluk</option>               </select>            </td>         </tr>                   <tr>            <td>Village: </td>            <td>               <selectid="villagelist"name="villagelist" required>                  <optiondisabled>Select Village</option>               </select>            </td>         </tr>      </table>   </body></html> | 
插件 jquery-3.6.0.min.js 需要导入到 HTML 项目中,当插件部署到本地项目并导入时,效果最佳。网页很简单,只有基本布局,没有太多 CSS,因为本教程的范围只是解释基于数据库的动态下拉列表。现在已经编写了下拉列表的网页布局,是时候编写 AJAX 调用了。名为 Ajaxcall.js 的 JavaScript 文件用于填充下拉列表。它也是调用链接到 Java 服务方法的 URL 的地方。
Ajax调用
- JavaScript
| var dis;var tal;var vill;$(document).ready(function () {   $.ajax({      type: "GET",      url: "http://localhost:8075/dist",      data: "json",      contentType: "application/json",      success: function (data) {         let obj = $.parseJSON(data);         $.each(obj, function (key, value) {            $('#districtlist').append('<option value="'+ value.districtcode + '">'+ value.districtcode + '--'+ value.districtname + '</option>');         });      },      error: function (data) {         $('#districtlist').append('<option>District Unavailable</option>');      },   });   /*$('#districtlist').trigger("change");*/   $('#districtlist').change(function () {      $('#taluklist').find('option').remove();      $('#taluklist').append('<option>Select taluk</option>');      $('#villagelist').find('option').remove();      $('#villagelist').append('<option>Select village</option>');      var distid1 = $('#districtlist').val();      var inputValObj = {};      alert(distid1);      inputValObj.Discode = distid1;      var inputVal = JSON.stringify(inputValObj);      alert(inputVal);      var data = inputVal.toString();      alert(data);      $.ajax({         type: "GET",         url: "http://localhost:8075/taluk?Discode=" + distid1,         /*data: 1,*/         contentType: "application/json",         success: function (data) {            let obj = $.parseJSON(data);            $.each(obj, function (key, value) {               $('#taluklist').append('<option value="'+ value.talukcode + '">'+ value.talukcode + '--'+ value.talukname + '</option>');            });         },         error: function (data) {            $('#taluklist').append('<option>Taluk Unavailable</option>');         },      });   });   $('#taluklist').change(function () {      $('#villagelist').find('option').remove();      $('#villagelist').append('<option>Select village</option>');      var distid1 = $('#districtlist').val();      var talukid = $('#taluklist').val();      alert(distid1);      alert(talukid);      var inputValObj = {};      inputValObj.Discode = distid1;      inputValObj.talucode = talukid;      var inputVal = JSON.stringify(inputValObj);      var data = inputVal.toString();      $.ajax({         type: "GET", //POST            url: "http://localhost:8075/village?Discode="+ distid1 + "&"+ "Talukcode=" + talukid,         contentType: "application/json",         success: function (data) {            let obj = $.parseJSON(data);            $.each(obj, function (key, value) {               $('#villagelist').append('<option value="'+ value.villagecode + '">'+ value.villagecode + '--'+ value.villagename + '</option>');            });         },         error: function (data) {            $('#villagelist').appe nd('<option>village Unavailable</option>');         },      });   });}); | 
当文档准备就绪时,使用“http://localhost:8075/dist”URL 在第一个 AJAX 调用中调用 saylistDistrict() 函数,提取的数据进入成功函数。现在,数据被解析并存储到变量“obj”中,然后使用 jQuery 中的 $.each 进行迭代。然后使用“$('#districtlist').append('<option value=”' + value.districtcode+ '”>' + value.districtcode + '–' + value.districtname+ '</option>');' 命令将迭代中的每个条目附加到区域下拉列表中。
注意:'districtlist' 是区域下拉菜单的 ID。
当地区下拉菜单发生变化时,jQuery '$('#districtlist').change(function () {}); 被调用,url “http://localhost:8075/taluk?Discode=”+distid1 调用 Java 函数 ListTaluk()。然后使用 ' $('#taluklist').append('<option value=”' + value.talukcode + '”>' + value.talukcode + '–' + value.talukname+ '</option>');' 命令将返回的数据填充到 taluk 下拉菜单中。
当 taluk 下拉列表值发生更改时,将调用 jQuery '$('#taluklist').change(function () {});'。url“http://localhost:8075/village?Discode=”+distid1+”&”+”Talukcode=”+talukid 调用 Java 函数 Listvillage()。然后使用 '$('#villagelist').append('<option value=”' + value.villagecode + '”>' + value.villagecode + '–' + value.villagename+ '</option>')' 命令将返回的数据填充到 'villagelist' 下拉列表中。
此外,每当修改下拉菜单时,依赖于修改的下拉菜单的其他下拉菜单值都会被删除,并插入“选择”占位符。使用 .remove() 函数删除下拉菜单值(如上例所示),并使用以下模板中的命令插入“选择”占位符“$('#taluklist').append('<option>Select taluk</option>');”。
在任何 Spring Boot 项目中,都会有一个带有 @SpringBootApplication 注释的 Java 类,必须使用右键单击并选择 Run As _> Java 应用程序来运行该类。第一个项目必须以这种方式运行。第二个项目必须在服务器上运行,方法是使用右键单击 - Run as -> Run on the server。使用以下方法运行 dropdown.jsp 后,可以观察到本教程的输出。



















