Skip to content

SpringBoot综合实验

1 实验类型

验证型,2学时,必选实验

2 实验目的

掌握Spring Boot项目的开发流程

熟悉Spring MVC、MyBatis、Freemarker等框架的使用

熟悉第三方依赖的引入方法

3 实验要求

使用SSM(Spring、Spring MVC、Mybatis)实现学生表的CRUD操作

可以自行选择框架组合,只要实现CRUD功能即可

4 实验环境

Windows 7 64、Open JDK 23+、Tomcat 11+、Spring Tools 4(STS4)、Visual Studio Code(VS Code)、Microsoft Edge/Chrome/Firefox等浏览器

5 实验步骤

创建Spring Boot项目

  1. 打开STS4,选择File -> New -> Spring Starter Project

  2. 输入项目参数;指定项目名称、选择Maven项目、选择Spring Boot版本、勾选Web、MySQL、MyBatis、Freemarker等依赖,点击Finish按钮创建项目

  3. 打开pom.xml,手工添加以下依赖:

依赖名 描述
spring-boot-starter-validation 有效性校验
validation-api 有效性校验
poi POI操作Excel
poi-ooxml POI操作Excel
sqlite-jdbc Sqlite驱动
Stu/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.6</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>
  <groupId>xust</groupId>
  <artifactId>Stu</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Stu</name>
  <description>Stu</description>
  <properties>
    <java.version>17</java.version>
  </properties>
  <dependencies>
    <!-- Starter for building MVC web applications using FreeMarker views -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
    <!-- Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat
    as the default embedded container -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 有效性校验 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>2.0.1.Final</version>
    </dependency>
    <!-- MyBatis Spring Boot Starter -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>3.0.1</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter-test</artifactId>
      <version>3.0.1</version>
      <scope>test</scope>
    </dependency>
    <!-- POI操作Excel -->
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>5.2.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>5.2.3</version>
    </dependency>
    <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml-schemas</artifactId>
      <version>4.1.2</version>
    </dependency>
    <!-- SQLite JDBC is a library for accessing and creating SQLite database files in Java (it
    includes native libraries) -->
    <dependency>
      <groupId>org.xerial</groupId>
      <artifactId>sqlite-jdbc</artifactId>
      <version>3.41.2.1</version>
    </dependency>
    <!-- 单元测试 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
    <!-- MyBatis 分页插件 PageHelper -->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper-spring-boot-starter</artifactId>
      <version>1.2.4</version>
    </dependency>
    <!-- JDBC Type 4 driver for MySQL. -->
    <dependency>
      <groupId>com.mysql</groupId>
      <artifactId>mysql-connector-j</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <scope>runtime</scope>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <!-- 修改maven远程仓库为国内服务器 -->
  <repositories>
    <repository>
      <id>public</id>
      <name>common central</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>public</id>
      <name>common central</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
  </pluginRepositories>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

配置

  1. 打开Stu/src/main/resources/application.properties文件,配置Sqlite数据库连接信息,参考代码如下:

    Sqlite数据库连接信息
    1
    2
    3
    # sqlite
    spring.datasource.url=jdbc:sqlite:d:/stu.db
    spring.datasource.driver-class-name=org.sqlite.JDBC
    
  2. 创建文件:Stu/src/main/resources/application-dev.properties文件,设置数据库初始化,参考代码如下:

    数据库初始化
    # sqlite
    spring.datasource.url=jdbc:sqlite:d:/stu-dev.db
    spring.datasource.driver-class-name=org.sqlite.JDBC
    spring.sql.init.schema-locations = classpath:sql/stu_schema.sql
    spring.sql.init.data-locations = classpath:sql/stu_data.sql
    spring.sql.init.mode=always
    
    logging.level.xust.*.dao=DEBUG
    
    #pagehelper
    pagehelper.helperDialect=sqlite
    pagehelper.reasonable=true
    pagehelper.supportMethodsArguments=true
    #pagehelper.params=count=countSql
    
  3. 设定Mybatis配置参数,参考代码如下:

    Mybatis配置参数
    1
    2
    3
    4
    #mybatis
    mybatis.type-aliases-package=xust.stu.**.domain
    mybatis.mapper-locations=classpath:mapper/*.xml
    mybatis.typeAliasesPackage=xust.stu.**.domain
    

完整配置文件如下:

完整配置文件
server.port=8080
server.address=0.0.0.0
server.servlet.context-path=/
spring.profiles.active=dev

## Time zone setting
spring.jackson.time-zone=GMT+8

#mybatis
mybatis.type-aliases-package=xust.stu.**.domain
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.typeAliasesPackage=xust.stu.**.domain

#freemarker
spring.freemarker.charset=UTF-8
spring.freemarker.suffix=.f

#Gzip
server.compression.enabled=true  
server.compression.mime-types=text/javascript,application/javascript,text/css

# sqlite
spring.datasource.url=jdbc:sqlite:d:/stu.db
spring.datasource.driver-class-name=org.sqlite.JDBC

创建学生表

  1. 编写学生表定义及测试数据SQL代码,存放在src/main/resources/sql

    stu_schema.sql
    1
    2
    3
    4
    5
    6
    7
    8
    9
    drop table if exists STUDENT;
    create table STUDENT(
        Id varchar(200) primary key,
        no varchar(20) unique  not null,
        name varchar(20) not null,
        gender varchar(2),
        age int,
        dept varchar(20)
    );
    
    stu_data.sql
    1
    2
    3
    4
    5
    6
    7
    8
    9
    delete from STUDENT;
    insert into STUDENT (Id,no,name,gender,age,dept) 
    values ('X0','X0','X0','X0',0,'X0'); 
    insert into STUDENT (Id,no,name,gender,age,dept) 
    values ('X1','X1','X1','X1',1,'X1'); 
    insert into STUDENT (Id,no,name,gender,age,dept) 
    values ('X2','X2','X2','X2',2,'X2'); 
    insert into STUDENT (Id,no,name,gender,age,dept) 
    values ('X3','X3','X3','X3',3,'X3'); 
    

    前步配置中已设置自动执行这两个脚本

  2. 创建实体类:xust.demo.stu.domain.Student

    xust.demo.stu.domain.Student类
    /*
     * File name : Student.java 2023-04-20
     * @generated May 28, 2023, 9:37:49 AM
     * @author XUST
     * Copyright 2023 XUST. All rights reserved.
     */
    package xust.demo.stu.domain;
    
    import javax.validation.constraints.DecimalMax;
    import javax.validation.constraints.DecimalMin;
    //import javax.validation.constraints.Email;
    import javax.validation.constraints.Max;
    import javax.validation.constraints.Min;
    //import javax.validation.constraints.NotEmpty;
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Size;
    import com.fasterxml.jackson.annotation.JsonFormat;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    
    /**
     * Class Student
     * 学生表
     * @author XUST
     * @version 1.0, 2023-04-20
     */
    public class Student{
      private static final long serialVersionUID = -8178158848305435288L;
      public static ObjectMapper objectMapper = new ObjectMapper();
    
    
      /**
       * Id
       */
      private String id;
    
      /**
       * 学号
       */
      @NotNull
      private String no;
    
      /**
       * 姓名
       */
      private String name;
    
      /**
       * 性别
       */
      private String gender;
    
      /**
       * 年龄
       */
      private Integer age;
    
      /**
       * 所在系
       */
      private String dept;
    
    
        /**
         * Id
         */
        public String getId(){
            return id;
        }
    
        /**
         * Id
         */
        public void setId(String newValue){
            id = newValue;
        }
        /**
         * 学号
         */
        public String getNo(){
            return no;
        }
    
        /**
         * 学号
         */
        public void setNo(String newValue){
            no = newValue;
        }
        /**
         * 姓名
         */
        public String getName(){
            return name;
        }
    
        /**
         * 姓名
         */
        public void setName(String newValue){
            name = newValue;
        }
        /**
         * 性别
         */
        public String getGender(){
            return gender;
        }
    
        /**
         * 性别
         */
        public void setGender(String newValue){
            gender = newValue;
        }
        /**
         * 年龄
         */
        public Integer getAge(){
            return age;
        }
    
        /**
         * 年龄
         */
        public void setAge(Integer newValue){
            age = newValue;
        }
        /**
         * 所在系
         */
        public String getDept(){
            return dept;
        }
    
        /**
         * 所在系
         */
        public void setDept(String newValue){
            dept = newValue;
        }
    
      @Override
      public String toString() {
        String res = "";
    
        try {
          res = objectMapper.writeValueAsString(this);
        } catch (Exception e) {
        }  
    
        return res;
      }
    }
    

实现CRUD操作

  1. 创建数据访问接口:xust.demo.stu.dao.StudentMapper,定义CRUD操作方法,参考代码如下:

    xust.demo.stu.dao.StudentMapper接口
    /*
     * File name : Student.java 2023-04-20
     * @generated May 28, 2023, 9:37:49 AM
     * @author XUST
     * Copyright 2023 XUST. All rights reserved.
     */
    package xust.demo.stu.dao;
    
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    import java.util.Map;
    import java.util.List;
    import xust.demo.stu.domain.Student;
    
    /**
     * StudentDao
     * @author XUST
     * @version 1.0, 2023-04-20
     */
    @Mapper
    public interface StudentDao {
    
      ///////////////////// Create
      /**
       * 创建对象
       * @param newValue 新对象
       * @return 影响行数,1为成功,0为失败
       */
      public int create(Student newValue);
    
      ///////////////////// Retrie
    
      /**
       * 获取对象数量
       * @param params 并参数
       * @return 非空新对象,非空Integer对象
       */
      public int size(@Param("params")Map<String, Object> params);
    
      /**
       * 获取对象分页
       * @param params 并参数
       * @return 非空对象列表
       */
      public List<Student> getPage(@Param("params")Map<String, Object> params, @Param("pageNum") int pageNum, @Param("pageSize") int pageSize);
    
      /**
       * 获取对象列表
       * @param items id列表
       * @return 非空对象列表
       */
      public List<Student> getItems(List<String> items);
    
      ///////////////////// Update
      /**
       * 更新对象
       * @param newValue 新值
       * @return 非空Integer对象
       */
      public int update(Student newValue);
    
      ///////////////////// Delete
      /**
       * 删除对象
       * @param ids id列表
       * @return 非空Integer对象
       */
      public int deleteItems(List<String> ids);
    
      /**
       * 删除对象
       * @return 非空Integer对象
       */
      public int deleteAll();
    
      public int delete(@Param("params")Map<String, Object> params);
    }
    
  2. 创建数据访问代码文件:Stu/src/main/resources/mapper/StudentMapper.xml文件,实现CRUD操作,参考代码如下:

    StudentMapper.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    
    <mapper namespace="xust.demo.stu.dao.StudentDao">
    
      <resultMap type="xust.demo.stu.domain.Student" id="studentResult">
        <result property="id" column="ID"></result>
        <result property="no" column="NO"></result>
        <result property="name" column="NAME"></result>
        <result property="gender" column="GENDER"></result>
        <result property="age" column="AGE"></result>
        <result property="dept" column="DEPT"></result>
      </resultMap>
    
      <!-- Create -->
      <insert id="create" parameterType="xust.demo.stu.domain.Student">
        <![CDATA[insert into Student(Id,no,name,gender,age,dept) 
        values(#{id},#{no},#{name},#{gender},#{age},#{dept})]]>
      </insert>
    
      <!-- Retriev data -->
      <!-- Retrieve data by id -->
      <select id="size" resultType="int" parameterType="map">
        <![CDATA[select count(*) from Student]]>
        <where>
          1=1
          <if test="params.id != null and params.id != ''">and Id = #{params.id}</if>
          <if test="params.ids != null and params.ids.size > 0">
            and id in 
            <foreach item="item" index="index" collection="params.ids" open="(" separator="," close=")">
            #{item}
            </foreach>
          </if>
        </where>
      </select>
    
      <!-- Retrieve data by page-->
      <select id="getPage" resultMap="studentResult">
        <![CDATA[select Id,no,name,gender,age,dept from Student]]>
        <where>
          1=1
          <if test="params.id != null and params.id != ''">and Id = #{params.id}</if>
          <if test="params.ids != null and params.ids.size > 0">
            and id in 
            <foreach item="item" index="index" collection="params.ids" open="(" separator="," close=")">
            #{item}
            </foreach>
          </if>
        </where>
        order by id desc
      </select>
    
        <!-- Retrieve data by page-->
      <select id="getItems" resultMap="studentResult">
        <![CDATA[select Id,no,name,gender,age,dept from Student where Id in ]]>
        <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
          #{item}
        </foreach>
        order by id desc
      </select>
    
      <!-- Update according to id -->
      <update id="update" parameterType="xust.demo.stu.domain.Student">
        <![CDATA[update Student set no=#{no},name=#{name},gender=#{gender},age=#{age},dept=#{dept} where Id = #{id}]]>
      </update>
    
      <!-- Delete -->
      <delete id="deleteItems">
        <![CDATA[delete from Student where Id in]]>
        <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
          #{item}
        </foreach>
      </delete>
    
      <delete id="deleteAll">
        <![CDATA[delete from Student]]>
      </delete>
    
      <delete id="delete">
        <![CDATA[delete from Student]]>
        <where>
          <if test="params.id != null">and id = #{params.id}</if>
          <if test="params.id == null">and id = '999999'</if>
        </where>
      </delete>
    
    </mapper>
    
  3. 创建服务接口:xust.demo.stu.service.StudentService接口和实现类:xust.demo.stu.service.StudentServiceImpl,实现CRUD操作,参考代码如下:

    xust.demo.stu.service.StudentService接口
    /*
     * File name : StudentService.java 2023-04-20
     * @generated May 28, 2023, 9:37:49 AM
     * @author XUST
     * Copyright 2023 XUST. All rights reserved.
     */
    package xust.demo.stu.service;
    
    import java.io.OutputStream;
    import java.util.Map;
    import java.util.List;
    
    import xust.Result;
    import xust.demo.stu.domain.Student;
    
    /**
     * Interface StudentService
     * Student service interface.
     * @author XUST
     * @version 1.0, 2023-04-20
     */
    public interface StudentService {
    
      /**
       * 创建对象
       * @param newValue 新对象
       * @return 影响行数,1为成功,0为失败
       */
      public Result<Student> create(Student newValue);
    
      /**
       * 删除所有数据
       * @return 删除行数
       */
      public Result<Integer> deleteAll();
    
      /*
       * 删除指定条件筛选的数据
       * @return 删除行数
       */
      public Result<Integer> delete(Map<String, Object> params);
    
      /**
       * 删除对象
       * @param ids id列表
       * @return 非空Integer对象
       */
      public Result<Integer> deleteItems(List<String> items);
    
      /**
       * 更新对象
       * @param newValue 新值
       * @return 非空Integer对象
       */
      public Result<Integer> update(Student newValue);
    
      /**
       * 获取对象数量
       * @param params 并参数
       * @return 非空新对象,非空Integer对象
       */
      public Result<Integer> size(Map<String, Object> params);
    
      /**
       * 获取对象分页
       * @param params 并参数
       * @param pageNum 页码(从1开始),超出范围会返回边界页
       * @param pageSize 分页大小
       * @return 非空对象列表
       */
      public Result<List<Student>> getPage(Map<String, Object> params, int pageNum, int pageSize);
    
      public Result<Boolean> export2XLS(List<Student> data, String worksheet_name, String title, OutputStream output);
    
    }
    
    xust.demo.stu.service.StudentServiceImpl实现类
    package xust.demo.stu.service;
    
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.UUID;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.apache.poi.hssf.usermodel.HSSFCell;
    import org.apache.poi.hssf.usermodel.HSSFRow;
    import org.apache.poi.hssf.usermodel.HSSFSheet;
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.ss.util.CellRangeAddress;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import xust.Result;
    import xust.ErrEnum;
    import xust.demo.stu.dao.StudentDao;
    import xust.demo.stu.domain.Student;
    
    /**
     * Class StudentServiceImpl
     * StudentService interface implementation.
     * @generated May 28, 2023, 9:37:49 AM
     * @author XUST
     * @version 1.0, 2023-04-20
     */
    @Service
    public class StudentServiceImpl implements StudentService{
      private final static Logger log = LoggerFactory.getLogger(StudentServiceImpl.class);
    
      @Autowired
      private StudentDao studentDao;
    
      @Override
      public Result<Student> create(Student newValue) {
        Result<Student> oc = new Result<Student>();
    
        try{
          //newValue.setSys_create_date(new Date());
          newValue.setId(UUID.randomUUID().toString());
          int effected = studentDao.create(newValue);
          if(effected > 0){
            oc.setData(newValue);
          }else{
            oc.setErr(ErrEnum.DB_CREATE_FAIL);
          }
        }catch(Exception e){
          oc.setErr(ErrEnum.DB_CREATE_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
    
        return oc;
      }
    
      @Override
      public Result<Integer> deleteAll() {
        Result<Integer> oc = new Result<Integer>();
    
        try{
          oc.setData(studentDao.deleteAll());
        }catch(Exception e){
          oc.setErr(ErrEnum.DB_DELETE_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
    
        return oc;
      }
    
      @Override
      public Result<Integer> deleteItems(List<String> items) {
        Result<Integer> oc = new Result<Integer>();
    
        try{
          oc.setData(studentDao.deleteItems(items));
        }catch(Exception e){
          oc.setErr(ErrEnum.DB_DELETE_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
    
        return oc;
      }
    
      @Override
      public Result<Integer> delete(Map<String, Object> params) {
        Result<Integer> oc = new Result<Integer>();
    
        try {
          oc.setData(studentDao.delete(params));
        }catch(Exception e) {
          oc.setErr(ErrEnum.DB_DELETE_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
    
        return oc;
      }
    
      @Override
      public Result<Integer> update(Student newValue) {
        Result<Integer> oc = new Result<Integer>();
        try{
          //newValue.setSys_last_update_date(new Date());
          oc.setData(studentDao.update(newValue));
        }catch(Exception e){
          oc.setErr(ErrEnum.DB_UPDATE_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
        return oc;
      }
    
      @Override
      public Result<Integer> size(Map<String, Object> params) {
        Result<Integer> oc = new Result<Integer>();
    
        try {
          oc.setData(studentDao.size(params));
        }catch(Exception e) {
          oc.setErr(ErrEnum.DB_SIZE_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
    
        return oc;
      }
    
      @Override
      public Result<List<Student>> getPage(Map<String, Object> params, int pageNum, int pageSize) {
        Result<List<Student>> oc = new Result<List<Student>>();
        oc.setData(new ArrayList<Student>());
    
        try {
          oc.setData(studentDao.getPage(params, pageNum, pageSize));
        }catch(Exception e) {
          oc.setErr(ErrEnum.DB_PAGE_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
        return oc;
      }
    
      public Result<Boolean> buildXLSTemplate(String worksheet_name, String title, OutputStream output) {
        Result<Boolean> oc = new Result<Boolean>();
    
        try {
          HSSFWorkbook wb = new HSSFWorkbook();
          HSSFSheet sheet = wb.createSheet(worksheet_name);
    
          // Header title
          HSSFRow row1 = sheet.createRow(0);
          HSSFCell cell = row1.createCell(0);
          cell.setCellValue(title);
          sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 5));
    
          // Header
          HSSFRow row2 = sheet.createRow(1);
          // 设置列标题
          row2.createCell(0).setCellValue("学号");
          row2.createCell(1).setCellValue("姓名");
          row2.createCell(2).setCellValue("性别");
          row2.createCell(3).setCellValue("年龄");
          row2.createCell(4).setCellValue("所在系");
    
          wb.write(output);
          //wb.close();
        } catch (Exception e) {
          oc.setErr(ErrEnum.EXPORT_EXCEL_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
    
        return oc;
      }
    
      @Override
      public Result<Boolean> export2XLS(List<Student> data, String worksheet_name, String title, OutputStream output) {
        Result<Boolean> oc = new Result<Boolean>();
    
        try {
          HSSFWorkbook wb = new HSSFWorkbook();
          HSSFSheet sheet = wb.createSheet(worksheet_name);
    
          // Header title
          HSSFRow row1 = sheet.createRow(0);
          HSSFCell cell = row1.createCell(0);
          cell.setCellValue(title);
          sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 5));
    
          // Header
          HSSFRow row2 = sheet.createRow(1);
          // 设置列标题
          row2.createCell(0).setCellValue("Id");
          row2.createCell(1).setCellValue("学号");
          row2.createCell(2).setCellValue("姓名");
          row2.createCell(3).setCellValue("性别");
          row2.createCell(4).setCellValue("年龄");
          row2.createCell(5).setCellValue("所在系");
    
          // 填充数据
          int row_number = 2;
          for (Student item : data) {
            HSSFRow row_new = sheet.createRow(row_number++);
            row_new.createCell(0).setCellValue(NullValue(item.getId()));
            row_new.createCell(1).setCellValue(NullValue(item.getNo()));
            row_new.createCell(2).setCellValue(NullValue(item.getName()));
            row_new.createCell(3).setCellValue(NullValue(item.getGender()));
            row_new.createCell(4).setCellValue(NullValue(item.getAge()));
            row_new.createCell(5).setCellValue(NullValue(item.getDept()));
          }
          wb.write(output);
          //wb.close();
        } catch (Exception e) {
          oc.setErr(ErrEnum.EXPORT_EXCEL_FAIL);
          oc.setDetail(e.getLocalizedMessage());
          log.error(e.getMessage());
        }
    
        return oc;
      }
    
      private String NullValue(Object o) {
        return null == o ? "" : o.toString();
      }
    }
    
  4. 创建控制类:StudentController,实现学生表CRUD操作Web Api(包名:xust.demo.stu.controller),参考代码如下:

    xust.demo.stu.controller.StudentController类
    /*
     * File name : StudentController.java 2023-04-20
     * Copyright 2023 XUST. All rights reserved.
     */
    package xust.demo.stu.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.net.URLEncoder;
    import jakarta.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.servlet.ModelAndView;
    
    import java.util.List;
    import org.springframework.validation.annotation.Validated;
    
    import xust.Result;
    import xust.Result4GetPage;
    import xust.demo.stu.domain.DeleteItems4Student;
    import xust.demo.stu.domain.GetPage4Student;
    import xust.demo.stu.domain.Student;
    import xust.demo.stu.service.StudentServiceImpl;
    
    import java.util.TreeMap;
    
    /**
     * Class StudentController
     * Student action class.
     * @generated May 28, 2023, 9:37:49 AM
     * @author XUST
     * @version 1.0, 2023-04-20
     */
    @RestController
    @RequestMapping("/demo/Student")
    public class StudentController{
      private final static Logger log = LoggerFactory.getLogger(StudentController.class);
    
      @Autowired
      private StudentServiceImpl studentService = null;
    
        @RequestMapping(value = "/list-jquery")
        public ModelAndView doListJquery(){
            ModelAndView mv = new ModelAndView("/demo/Student/list-jquery");
    
            return mv;
        }
        @RequestMapping(value = "/list-bootstrap")
        public ModelAndView doListBootstrap(){
            ModelAndView mv = new ModelAndView("/demo/Student/list-bootstrap");
    
            return mv;
        }
    
        @PostMapping(value = "/add")
        public Result<Student> doAdd(@RequestBody(required=true) @Validated Student o){
            Result<Student> oc = studentService.create(o);
    
            return oc;
        }
    
        @PostMapping(value="/delete")
        public Result<Integer> doDelete(@RequestBody(required=true) List<String> ids){
            Result<Integer> oc = studentService.deleteItems(ids);
    
            return oc;
        }
    
        @PostMapping(value="/update")
        public Result<Integer> doUpdate(@RequestBody(required=true) @Validated Student o){
            Result<Integer> oc = studentService.update(o);
    
            return oc;
        }
    
        @PostMapping(value="/getPage")
        public Result<Result4GetPage<List<Student>>> doGetPage(@RequestBody(required=true) @Validated GetPage4Student p){
            Result<Result4GetPage<List<Student>>> res = new Result<Result4GetPage<List<Student>>>();
    
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("id", p.getId());
            Result<Integer> oc_size = studentService.size(params);
            if(oc_size.getCode() == 0){
                Result<List<Student>> oc_getPage = studentService.getPage(params, p.getPageNum(), p.getPageSize());
                if(oc_getPage.getCode() == 0){
                    Result4GetPage<List<Student>> result = new Result4GetPage<List<Student>>();
                    result.setTotal(oc_size.getData());
                    result.setData(oc_getPage.getData());
                    res.setData(result);
                }else{
                    res.setCode(oc_getPage.getCode());
                    res.setMessage(oc_getPage.getMessage());
                }
            }else{
                res.setCode(oc_size.getCode());
                res.setMessage(oc_size.getMessage());
            }
    
            return res;
        }
    
        @PostMapping(value="/template")
        public void doTemplate(HttpServletResponse response){
            try {
                String file_name = "学生表.xls";
                response.setContentType("application/vnd.ms-excel;charset=utf-8");
                response.setHeader("Content-disposition", "attachment;filename=" + file_name);
                response.flushBuffer();
                OutputStream outputStream = response.getOutputStream();
                studentService.buildXLSTemplate("学生表", "学生表", outputStream);
                outputStream.flush();
                outputStream.close();
            } catch (Exception e) {
                log.error(e.getLocalizedMessage());
            }
        }
    
        @PostMapping(value="/toExcel")
        public void doToExcel(HttpServletResponse response, @RequestBody(required=true) @Validated GetPage4Student p){
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("id", p.getId());
            Result<Integer> oc_size = studentService.size(params);
            String title="学生表";
            OutputStream outputStream = null;
            if(oc_size.getCode() == 0){     
                Result<List<Student>> oc_getPage = studentService.getPage(params, 1, 2000);
                if(oc_getPage.getCode() == 0){
            try {
              outputStream = response.getOutputStream();
              response.setContentType("application/vnd.ms-excel;charset=utf-8");
              String fileName = URLEncoder.encode(title + ".xls", "UTF-8");
              response.setHeader("Content-disposition", "attachment;filename=" + fileName);
              response.flushBuffer();
              studentService.export2XLS(oc_getPage.getData(), title, title, outputStream);
              outputStream.flush();
              outputStream.close();
            } catch (Exception e) {
              log.error("导出Excel异常", e);
            }finally {
              try {
                if(outputStream != null){
                  outputStream.close();
                }
              } catch (Exception e1) {
                log.error("导出Excel关闭异常", e1);
              }
            }
                }
            }
        }
    }
    

测试

  1. 在Spring Boot Dashbord启动Stu工程

  2. 使用CRUD测试代码:Stu/api_test.http(Rest Client代码),参考代码如下:

    Stu/api_test.http
    ###
    POST http://localhost:8080/demo/Student/add HTTP/1.1
    content-type: application/json
    
    {
      "id": "X100",
      "no": "99999",
      "name": "X102",
      "gender": "X103",
      "age": 104,
      "dept": "X105"
    }
    
    ###
    POST http://localhost:8080/demo/Student/delete HTTP/1.1
    content-type: application/json
    
    ["1", "2"]
    
    
    ###
    POST http://localhost:8080/demo/Student/update HTTP/1.1
    content-type: application/json
    
    {
      "id": "X100",
      "no": "X101",
      "name": "X102",
      "gender": "X103",
      "age": 104,
      "dept": "X105"
    }
    
    ###
    POST http://localhost:8080/demo/Student/getPage HTTP/1.1
    content-type: application/json
    
    {
        "pageNum": 1,
        "pageSize": 5
    }
    
  3. 分别点击各REST Client链接,检查返回结果是否正确

Stu/api.md中包含各Web Api的使用说明,可据此检查返

学生表

学生表增

调用地址: http://my_host:8080/Demo/stu/Student/add

请求方式: POST

返回类型: JSON

请求参数(Headers)

参数
content-type application/json

请求参数(Query)

请求参数(Body)

{
  "id": String,  //Id
  "no": String,  //学号
  "name": String,  //姓名
  "gender": String,  //性别
  "age": Integer,  //年龄
  "dept": String  //所在系
}

请求示例

POST http://localhost:8080/Demo/stu/Student/add HTTP/1.1
content-type: application/json

{
  "id": "X100",
  "no": "X101",
  "name": "X102",
  "gender": "X103",
  "age": 104,
  "dept": "X105"
}

Rest Client请求格式

正常返回示例

{
  "code": 0,
  "message": "操作成功!",
  "detail": null,
  "data": 1
}

失败返回示例

{
  "code": 1001,
  "message": "创建失败!",
  "detail": "[SQLITE_CONSTRAINT_UNIQUE] A UNIQUE constraint failed (UNIQUE constraint failed: Student.no)",
  "data": null
}

错误码定义

错误码 错误信息 描述
0 成功
学生表删

调用地址: http://my_host:8080/Demo/stu/Student/delete

请求方式: POST

返回类型: JSON

请求参数(Headers)

参数
content-type application/json

请求参数(Query)

请求参数(Body)

["id1", "id2"]

请求示例

POST http://localhost:8080/Demo/stu/Student/delete HTTP/1.1
content-type: application/json

["1", "2"]

Rest Client请求格式

正常返回示例

{
  "code": 0,
  "message": "操作成功!",
  "detail": null,
  "data": 1
}

失败返回示例

{
  "code": 0,
  "message": "操作成功!",
  "detail": null,
  "data": 0 //影响行数为0
}

错误码定义

错误码 错误信息 描述
0 成功
学生表改

调用地址: http://my_host:8080/Demo/stu/Student/update

请求方式: POST

返回类型: JSON

请求参数(Headers)

参数
content-type application/json

请求参数(Query)

请求参数(Body)

{
  "id": String,  //Id
  "no": String,  //学号
  "name": String,  //姓名
  "gender": String,  //性别
  "age": Integer,  //年龄
  "dept": String  //所在系
}

请求示例

POST http://localhost:8080/Demo/stu/Student/update HTTP/1.1
content-type: application/json

{
  "id": "X100",
  "no": "X101",
  "name": "X102",
  "gender": "X103",
  "age": 104,
  "dept": "X105"
}

Rest Client请求格式

正常返回示例

{
  "code": 0,
  "message": "操作成功!",
  "detail": null,
  "data": 1
}

失败返回示例

{
  "code": 1001,
  "message": "创建失败!",
  "detail": "[SQLITE_CONSTRAINT_UNIQUE] A UNIQUE constraint failed (UNIQUE constraint failed: Student.no)",
  "data": null
}

错误码定义

错误码 错误信息 描述
0 成功
学生表查

调用地址: http://my_host:8080/Demo/stu/Student/getPage

请求方式: POST

返回类型: JSON

请求参数(Headers)

参数
content-type application/json

请求参数(Query)

请求参数(Body)

{
    "pageNum": Integer, //页码,从0开始
    "pageSize": Integer //页大小
}

请求示例

POST http://localhost:8080/Demo/stu/Student/getPage HTTP/1.1
content-type: application/json

{
    "pageNum": 1,
    "pageSize": 5
}

Rest Client请求格式

正常返回示例

{
  "code": 0,
  "message": "操作成功!",
  "detail": null,
  "data": {
    "total": 4,
    "data": [
      {
        "id": "X100",
        "no": "X101",
        "name": "X102",
        "gender": "X103",
        "age": 104,
        "dept": "X105"
      }
    ]
  }
}

失败返回示例

{
  "code": 0,
  "message": "操作成功!",
  "detail": null,
  "data": {
    "total": 0,
    "data": []
  }
}

错误码定义

错误码 错误信息 描述
0 成功

基于jQuery的界面

  1. 打开Stu/src/main/resources/templates目录,创建目录:demo/Student

  2. 创建list-jquery.f文件,用于显示学生表,参考代码如下:

    list-jquery.f
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8">
      <title>学生表表管理</title>
    </head>
    
    <body>
      <h3>学生表信息管理</h3>
      <div id="queryForm">
        <label for="id">id</label>
        <input type="text" id="id" name="id" value="">&nbsp;
        <button id="buttonQuery">查询</button>
        <button id="buttonAdd">添加</button>
      </div>
    
      <!-- 学生表列表 -->
      <table id="objTable">
        <thead>
          <tr>
            <th style="display:none;">Id</th>
            <th>学号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>所在系</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <!-- 这里用jQuery动态生成表格行 -->
        </tbody>
      </table>
    </body>
    
    <div id="objModel">
      <div id="objModelContent">
        <span id="close">&times;</span>
        <h2 id="updateTitle">修改学生表记录</h2>
        <div id="updateForm">
          <input type="hidden" id="update-id" name="id">
          <div class="mb-3">
            <label for="update-no" class="form-label">学号</label>
            <input type="text" id="update-no" name="no" required>
          </div>
          <div class="mb-3">
            <label for="update-name" class="form-label">姓名</label>
            <input type="text" id="update-name" name="name" required>
          </div>
          <div class="mb-3">
            <label for="update-gender" class="form-label">性别</label>
            <input type="text" id="update-gender" name="gender" required>
          </div>
          <div class="mb-3">
            <label for="update-age" class="form-label">年龄</label>
            <input type="text" id="update-age" name="age" required>
          </div>
          <div class="mb-3">
            <label for="update-dept" class="form-label">所在系</label>
            <input type="text" id="update-dept" name="dept" required>
          </div>
          <button id="updateSave">保存</button>
        </div>
      </div>
    </div>
    
    <!-- 弹出框 -->
    <div id="popup">
      <div id="popupHeader">
        <h3 id="popupTitle">消息</h3>
        <button id="popupClose">X</button>
      </div>
      <div id="popupBody">
        <p>消息内容</p>
      </div>
    </div>
    
    <script src="/jquery/jquery-3.6.1.min.js"></script>
    <script>
      const idsUpdate = ["update-id","update-no","update-name","update-gender","update-age","update-dept"];
    
      $(document).ready(function () {
        getPage();
    
        //注册查询
        $('#buttonQuery').click(function () {
          getPage()
        });
    
        $('#buttonAdd').click(function () {
          showModel4Add();
        });
    
        //注册关闭错误信息
        $('#popupClose').click(function () {
          $('#popup').hide();
        })
        //注册关闭错误信息
        $('#close').click(function () {
          $('#objModel').hide();
        })
      });
    
      /**
       * 从修改对话框提取学生表信息
       */
      function loadFromUI(ids) {
        var res = {};
        ids.forEach((id) => {
          res[id.match(/-(\w+)/)[1]] = $("#" + id).val();
        });
    
        return res;
      }
    
      /**
       * 将学生表信息填充到有val属性的界面
       */
      function fill2Val(ids, obj) {
        ids.forEach((id) => {
          //id约定以成员名结束,如-id
          $("#" + id).val(obj[id.match(/-(\w+)$/)[1]]);
        });
      }
      /**
       * 将学生表信息填充到有text属性的界面
       */
      function fill2Text(ids, obj) {
        ids.forEach((id) => {
          //id约定以成员名结束,如-id
          $("#" + id).text(obj[id.match(/-(\w+)$/)[1]]);
          //console.log(id + ":" + id.match(/-(\w+)$/)[1] + "=" + obj[id.match(/-(\w+)$/)[1]]);
        });
      }
    
      function buildRowIds(id) {
        return [
            "row-" + id + "-id",
            "row-" + id + "-no",
            "row-" + id + "-name",
            "row-" + id + "-gender",
            "row-" + id + "-age",
            "row-" + id + "-dept"
        ];
      }
    
      /**
       * 显示修改学生表信息对话框
       */
      function update(id) {
        // 获取学生表信息
        $.ajax({
          url: '/demo/Student/getPage',
          type: 'POST',
          dataType: "json",
          contentType: 'application/json',
          data: JSON.stringify({
            "id": id,
            "pageNum": 1,
            "pageSize": 1
          }),
          success: function (response) {
            if (response.code == 0) {
              $.each(response.data.data, function (index, obj) {
                showModel4Update(obj);
              });
            } else {
              showError(response.message);
            }
          },
          error: function (error) {
            showError(error.responseJSON.message);
          }
        });
      }
    
      /**
       * 学生表信息对话框用作修改
       */
      function showModel4Update(obj) {
        fill2Val(idsUpdate, obj);
        $('#updateTitle').text("修改学生表信息");
        $('#updateSave').text("修改");
        //注册修改
        $('#updateSave').off();
        $('#updateSave').click(function () {
          var obj = loadFromUI(idsUpdate);
          updateObj(obj)
        });
        $("#objModel").show();
      }
    
      /**
       * 学生表信息对话框用作增加
       */
      function showModel4Add() {
        fill2Val(idsUpdate, {
          "id": "",
          "no": "",
          "name": "",
          "gender": "",
          "age": "",
          "dept": ""
        });
        $('#updateTitle').text("新建学生表信息");
        $('#updateSave').text("新建");
        //注册增加
        $('#updateSave').off();
        $('#updateSave').click(function () {
          var obj = loadFromUI(idsUpdate);
          addObj(obj)
        });
        $("#objModel").show();
      }
    
      /**
       * 增加学生表信息
       */
      function addObj(obj) {
        // 获取学生表信息
        $.ajax({
          url: '/demo/Student/add',
          type: 'POST',
          dataType: "json",
          contentType: 'application/json',
          data: JSON.stringify({
            "id" : obj.id,
            "no" : obj.no,
            "name" : obj.name,
            "gender" : obj.gender,
            "age" : obj.age,
            "dept" : obj.dept
          }),
          success: function (response) {
            if (response.code == 0) {
              const obj_new = response.data;
              appendRow(obj_new);
              fill2Text(buildRowIds(obj_new.id), obj_new);
              $("#objModel").hide();
            }
            showError(response.message);
          },
          error: function (error) {
            showError(error.responseJSON.message);
          }
        });
      }
    
      /**
       * 修改学生表信息
       */
      function updateObj(obj) {
        // 获取学生表信息
        $.ajax({
          url: '/demo/Student/update',
          type: 'POST',
          dataType: "json",
          contentType: 'application/json',
          data: JSON.stringify({
            "id" : obj.id,
            "no" : obj.no,
            "name" : obj.name,
            "gender" : obj.gender,
            "age" : obj.age,
            "dept" : obj.dept
          }),
          success: function (response) {
            if (response.code == 0) {
              fill2Text(buildRowIds(obj.id), obj);
              $("#objModel").hide();
            }
            showError(response.message);
          },
          error: function (error) {
            showError(error.responseJSON.message);
          }
        });
      }
    
      /**
       * 获取学生表信息
       */
      function getPage() {
        // 获取学生表信息
        $.ajax({
          url: '/demo/Student/getPage',
          type: 'POST',
          dataType: "json",
          contentType: 'application/json',
          data: JSON.stringify({
            "id": $('#id').val(),
            "pageNum": 1,
            "pageSize": 1
          }),
          success: function (response) {
            if (response.code == 0) {
              $('#objTable tbody').empty();
              // 遍历学生表信息,生成表格行
              $.each(response.data.data, function (index, obj) {
                appendRow(obj);
              });
            } else {
              showError(response.message);
            }
    
          },
          error: function (error) {
            showError(error.responseJSON.message);
          }
        });
      }
    
      /**
       * 学生表列表中新增一行
       */
      function appendRow(obj) {
        var tr = $('<tr id="row-' + obj.id + '">');
        tr.append('<td style="display:none;" id="row-' + obj.id + '-id">' + obj.id + '</td>');
        tr.append('<td id="row-' + obj.id + '-no">' + obj.no + '</td>');
        tr.append('<td id="row-' + obj.id + '-name">' + obj.name + '</td>');
        tr.append('<td id="row-' + obj.id + '-gender">' + obj.gender + '</td>');
        tr.append('<td id="row-' + obj.id + '-age">' + obj.age + '</td>');
        tr.append('<td id="row-' + obj.id + '-dept">' + obj.dept + '</td>');
        tr.append('<td><button onclick="update(\'' + obj.id + '\')">编辑</button><button onclick="del(\'' + obj.id + '\')">删除</button></td>');
        $('#objTable tbody').append(tr);
      }
    
      /*
       * 删除
       */
      function del(id) {
        $.ajax({
          url: "/demo/Student/delete",
          type: "POST",
          contentType: "application/json",
          data: JSON.stringify([id]),
          success: function (response) {
            showError(response.message);
            $("#row-" + id).remove();
          },
          error: function (xhr, status, error) {
            showError(error.responseJSON.message);
          }
        });
    
      }
    
      function showError(message) {
        $('#popupBody').text(message);
        $('#popup').show();
      }
    </script>
    
    <!-- 学生表列表 -->
    <style>
      #objTable {
        border-collapse: collapse;
        width: 100%;
      }
    
      #objTable th,
      #objTable td {
        border: 1px solid #ddd;
        padding: 8px;
        text-align: center;
      }
    
      #objTable th {
        background-color: #f2f2f2;
      }
    </style>
    
    <!-- 学生表信息框 -->
    <style>
      /* 模式对话框样式 */
      #objModel {
        display: none;
        position: fixed;
        top: 50%;
        left: 50%;
        z-index: 666;
        overflow: auto;
        background-color: #fff;
        border: 1px solid #ccc;
        padding: 10px;
      }
    
      #modalContent {
        background-color: #fff;
        margin: 10% auto;
        padding: 20px;
        border: 1px solid #888;
        width: 50%;
      }
    
      #close {
        color: #aaa;
        float: right;
        font-size: 28px;
        font-weight: bold;
      }
    
      #close:hover,
      #close:focus {
        color: black;
        text-decoration: none;
        cursor: pointer;
      }
    </style>
    <!-- 错误消息框 -->
    <style>
      #popup {
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: #fff;
        border: 1px solid #ccc;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
        z-index: 9999;
        display: none;
      }
    
      #popupHeader {
        padding: 10px;
        background-color: #f5f5f5;
        border-bottom: 1px solid #ccc;
        position: relative;
      }
    
      #popupTitle {
        margin: 0;
      }
    
      #popupClose {
        position: absolute;
        top: 5px;
        right: 5px;
        border: none;
        background-color: transparent;
        font-size: 20px;
        cursor: pointer;
      }
    
      #popupBody {
        padding: 10px;
      }
    </style>
    
    </html>
    
  3. 在浏览器中打开http://localhost:8080/demo/Student/list-jquery

基于Bootstrap的界面

  1. 打开Stu/src/main/resources/templates目录

  2. 创建list-bootstrap.f文件,用于显示学生表,参考代码如下:

    list-bootstrap.f
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="utf-8">
      <title>学生表管理</title>
      <!-- 引入Bootstrap 5和相关的JavaScript库 -->
      <link rel="stylesheet" href="/bootstrap-5.2.3-dist/css/bootstrap.min.css">
        <style>
        html,
        body {
          height: 100%;
        }
    
        .row {
          height: 100%;
        }
    
        .container-fluid {
          height: 100%;
        }
      </style>
    </head>
    
    <body>
    
      <h3>学生表信息管理</h3>
      <nav class="navbar navbar-expand-sm navbar-light bg-light">
    
        <div class="row g-3 align-items-center">
          <div class="input-group input-group-sm" style="width: 250px;">
            <span class="input-group-text" id="label-id">id</span>
            <input type="text" class="form-control" id="id" name="id" value="" placeholder="id" aria-label="id"
              aria-describedby="label-id" style="width: 150px;">
            <!--
            <button class="btn btn-outline-secondary" type="button" id="btn-label-no-clear">清除</button>
            -->
          </div>
          <div class="col-auto">
            <button id="buttonQuery" class="btn btn-primary btn-sm">查询</button>
          </div>
          <div class="col-auto">
            <button id="buttonAdd" class="btn btn-success btn-sm">添加</button>
          </div>
        </div>
      </nav>
    
      <!-- 学生表列表 -->
      <table class="table table-bordered table-striped">
        <thead>
          <tr>
            <th style="display:none;">Id</th>
            <th>学号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>所在系</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody id="objTableBody">
          <!-- 这里用JavaScript动态生成表格行 -->
        </tbody>
    
      </table>
    
      <nav class="navbar navbar-expand-sm navbar-light bg-light fixed-bottom bg-body-tertiary">
        <div class="row g-3 align-items-center">
          <div class="input-group input-group-sm" style="width: 150px;">
            <label class="input-group-text" for="pageSize">每页显示:</label>
            <select class="form-control" id="pageSize" name="pageSize" required>
              <option value="10" selected>10</option>
              <option value="30">30</option>
              <option value="100">100</option>
            </select>
          </div>
          <div class="input-group input-group-sm" style="width: 150px;">
            <label class="input-group-text" for="total">总页数:</label>
            <input type="number" min="1" class="form-control" id="total" value="0" disabled>
          </div>
          <div class="input-group input-group-sm" style="width: 150px;">
            <label class="input-group-text" for="pageSize">当前页码:</label>
            <input type="number" min="1" class="form-control" id="pageNum" value="0">
          </div>
          <div class="col-auto">
            <button id="buttonTo" class="btn btn-success btn-sm">跳转</button>
          </div>
        </div>
      </nav>
    
      <!-- 学生表信息对话框 -->
      <div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" id="objModel" tabindex="-1"
        aria-labelledby="objModelTitle" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered">
          <div class="modal-content">
            <div class="modal-header">
              <h5 class="modal-title" id="objModelTitle">修改学生表记录</h5>
              <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
              <form id="updateForm">
                <input type="hidden" id="update-id" name="id">
                <div class="mb-3">
                  <label for="update-no" class="form-label">学号</label>
                  <input type="text" class="form-control" id="update-no" name="no" required>
                </div>
                <div class="mb-3">
                  <label for="update-name" class="form-label">姓名</label>
                  <input type="text" class="form-control" id="update-name" name="name" required>
                </div>
                <div class="mb-3">
                  <label for="update-gender" class="form-label">性别</label>
                  <input type="text" class="form-control" id="update-gender" name="gender" required>
                </div>
                <div class="mb-3">
                  <label for="update-age" class="form-label">年龄</label>
                  <input type="text" class="form-control" id="update-age" name="age" required>
                </div>
                <div class="mb-3">
                  <label for="update-dept" class="form-label">所在系</label>
                  <input type="text" class="form-control" id="update-dept" name="dept" required>
                </div>
              </form>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
              <button type="button" class="btn btn-primary" id="updateSave">保存</button>
            </div>
          </div>
        </div>
      </div>
    
      <!-- 错误信息弹窗 -->
      <div class="modal fade" id="errorModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
        aria-labelledby="errorModalTitle" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered">
          <div class="modal-content">
            <!--
            <div class="modal-header">
              <h5 class="modal-title" id="errorModalTitle">信息</h5>
              <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            -->
            <div class="modal-body" id="errorModalBody">
              <!-- 这里用JavaScript动态生成错误信息 -->
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
            </div>
          </div>
        </div>
      </div>
      <script src="/bootstrap-5.2.3-dist/js/bootstrap.bundle.min.js"></script>
      <script src="/jquery/jquery-3.6.1.min.js"></script>
      <script>
        const idsUpdate = ["update-id","update-no","update-name","update-gender","update-age","update-dept"];
    
        $(document).ready(function () {
          $('#pageNum').val(1);
          getPage();
    
          //注册查询
          $('#buttonQuery').click(function () {
            $('#pageNum').val(1);
            getPage()
          });
    
          $('#buttonAdd').click(function () {
            showModel4Add();
          });
    
          $('#updateSave').click(function () {
            var obj = loadFromUI(idsUpdate);
            addObj(obj)
          });
    
          $('#buttonTo').click(function () {
            getPage();
          });
    
          //注册关闭错误信息
          $('#close').click(function () {
            $('#objModel').modal("hide");
          })
        });
    
        /**
         * 从修改对话框提取学生表信息
         */
        function loadFromUI(ids) {
          var res = {};
          ids.forEach((id) => {
            res[id.match(/-(\w+)/)[1]] = $("#" + id).val();
          });
    
          return res;
        }
    
        /**
         * 将学生表信息填充到有val属性的界面
         */
        function fill2Val(ids, obj) {
          ids.forEach((id) => {
            //id约定以成员名结束,如-id
            $("#" + id).val(obj[id.match(/-(\w+)$/)[1]]);
          });
        }
        /**
         * 将学生表信息填充到有text属性的界面
         */
        function fill2Text(ids, obj) {
          ids.forEach((id) => {
            //id约定以成员名结束,如-id
            $("#" + id).text(obj[id.match(/-(\w+)$/)[1]]);
            //console.log(id + ":" + id.match(/-(\w+)$/)[1] + "=" + obj[id.match(/-(\w+)$/)[1]]);
          });
        }
    
        function buildRowIds(id) {
          return [
            "row-" + id + "-id",
            "row-" + id + "-no",
            "row-" + id + "-name",
            "row-" + id + "-gender",
            "row-" + id + "-age",
            "row-" + id + "-dept"
          ];
        }
        /**
         * 显示修改学生表信息对话框
         */
        function update(id) {
          // 获取学生表信息
          $.ajax({
            url: '/demo/Student/getPage',
            type: 'POST',
            dataType: "json",
            contentType: 'application/json',
            data: JSON.stringify({
              "id": id,
              "pageNum": 1,
              "pageSize": 1
            }),
            success: function (response) {
              if (response.code == 0) {
                $.each(response.data.data, function (index, obj) {
                  showModel4Update(obj);
                });
              } else {
                showError(response.message);
              }
            },
            error: function (error) {
              showError(error.responseJSON.message);
            }
          });
        }
    
        /**
         * 学生表信息对话框用作修改
         */
        function showModel4Update(obj) {
          fill2Val(idsUpdate, obj);
          $('#updateTitle').text("修改学生表信息");
          $('#updateSave').text("修改");
          //注册修改
          $('#updateSave').off();
          $('#updateSave').click(function () {
            var obj = loadFromUI(idsUpdate);
            updateObj(obj)
          });
          $("#objModel").modal("show");
        }
    
        /**
         * 学生表信息对话框用作增加
         */
        function showModel4Add() {
          fill2Val(idsUpdate, {
            "id" : "",
            "no" : "",
            "name" : "",
            "gender" : "",
            "age" : "",
            "dept" : ""
          });
          $('#updateTitle').text("新建学生表信息");
          $('#updateSave').text("新建");
          //注册增加
          $('#updateSave').off();
          $('#updateSave').click(function () {
            var obj = loadFromUI(idsUpdate);
            addObj(obj)
          });
          $("#objModel").modal("show");
        }
    
        /**
         * 增加学生表信息
         */
        function addObj(obj) {
          // 获取学生表信息
          $.ajax({
            url: '/demo/Student/add',
            type: 'POST',
            dataType: "json",
            contentType: 'application/json',
            data: JSON.stringify({
              "id" : obj.id,
              "no" : obj.no,
              "name" : obj.name,
              "gender" : obj.gender,
              "age" : obj.age,
              "dept" : obj.dept
            }),
            success: function (response) {
              if (response.code == 0 && response.data != null) {
                const obj_new = response.data;
                appendRow(obj_new);
                fill2Text(buildRowIds(obj_new.id), obj_new);
                $("#objModel").modal("hide");
              }
              showError(response.message);
            },
            error: function (error) {
              showError(error.responseJSON.message);
            }
          });
        }
    
        /**
         * 修改学生表信息
         */
        function updateObj(obj) {
          // 获取学生表信息
          $.ajax({
            url: '/demo/Student/update',
            type: 'POST',
            dataType: "json",
            contentType: 'application/json',
            data: JSON.stringify({
              "id" : obj.id,
              "no" : obj.no,
              "name" : obj.name,
              "gender" : obj.gender,
              "age" : obj.age,
              "dept" : obj.dept
            }),
            success: function (response) {
              if (response.code == 0 && response.data > 0) {
                fill2Text(buildRowIds(obj.id), obj);
                $("#objModel").modal("hide");
              }
              showError(response.message);
            },
            error: function (error) {
              showError(error.responseJSON.message);
            }
          });
        }
    
        /**
         * 获取学生表信息
         */
        function getPage() {
          // 获取学生表信息
          const pageNum = $('#pageNum').val();
          const pageSize = $('#pageSize').val();
          $.ajax({
            url: '/demo/Student/getPage',
            type: 'POST',
            dataType: "json",
            contentType: 'application/json',
            data: JSON.stringify({
              "id": $('#id').val(),
              "pageNum": pageNum,
              "pageSize": pageSize
            }),
            success: function (response) {
              if (response.code == 0) {
                $('#objTableBody').empty();
                // 遍历学生表信息,生成表格
                $.each(response.data.data, function (index, obj) {
                  appendRow(obj);
                });
    
                $('#pagination').empty();
                //更新分页
                var total = Math.ceil(response.data.total / parseInt(pageSize));
                $('#total').val(total);
                for (var i = 1; i <= total; i++) {
                  var li = $("<li class='page-item'></li>");
                  var a = $("<a class='page-link' href='#'>" + i + "</a>");
                  a.click(function () {
                    $('#pageNum').val($(this).text());
                    getPage();
                  });
                  li.append(a);
                  $("#pagination").append(li);
                }
              } else {
                showError(response.message);
              }
    
            },
            error: function (error) {
              showError(error.responseJSON.message);
            }
          });
        }
    
        /**
         * 学生表列表中新增一行
         */
        function appendRow(obj) {
          var tr = $('<tr id="row-' + obj.id + '">');
          tr.append('<td style="display:none;" id="row-' + obj.id + '-id">' + obj.id + '</td>');
          tr.append('<td id="row-' + obj.id + '-no">' + obj.no + '</td>');
          tr.append('<td id="row-' + obj.id + '-name">' + obj.name + '</td>');
          tr.append('<td id="row-' + obj.id + '-gender">' + obj.gender + '</td>');
          tr.append('<td id="row-' + obj.id + '-age">' + obj.age + '</td>');
          tr.append('<td id="row-' + obj.id + '-dept">' + obj.dept + '</td>');
          tr.append('<td><button type="button" class="btn btn-outline-primary btn-sm" onclick="update(\'' + obj.id + '\')">编辑</button><button type="button" class="btn btn-outline-danger btn-sm" onclick="del(\'' + obj.id + '\')">删除</button></td>');
          $('#objTableBody').append(tr);
        }
    
        /*
         * 删除
         */
        function del(id) {
          $.ajax({
            url: "/demo/Student/delete",
            type: "POST",
            contentType: "application/json",
            data: JSON.stringify([id]),
            success: function (response) {
              showError(response.message);
              if (response.code == 0 && response.data > 0) {
                $("#row-" + id).remove();
              }
            },
            error: function (xhr, status, error) {
              showError(error.responseJSON.message);
            }
          });
    
        }
    
        function showError(message) {
          $('#errorModalBody').text(message);
          $('#errorModal').modal("show");
        }
      </script>
    </body>
    
    </html>
    
  3. 在浏览器中打开http://localhost:8080/demo/Student/list-bootstrap

拓展练习

  1. 实现课程表的增、删、改、查操作