ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSP+Servlet实现成绩管理系统:架构、避坑与工程实践

JSP+Servlet实现成绩管理系统:架构、避坑与工程实践 简介基于JSPServlet与MySQL的学生成绩管理系统完整项目面向Java Web课程设计、毕业设计及初学开发者。系统按角色划分为学生、教师、管理员三大模块学生可查询个人信息与本人成绩教师可录入、修改、查看并分析成绩管理员则负责学生、教师、班级、课程的增删改查以及课程安排、用户管理和班级分配。压缩包内共870个文件包含93个Java源文件、46个JSP页面、174个CSS样式、212个JavaScript脚本及大量图片资源另有SQL数据库脚本与项目文档整体约39.64MB目录结构完整便于直接导入运行和学习参考。目前已有173人学习下载可作为Java Web分角色权限管理的综合实践范本帮助理解Servlet控制层与JSP视图层的配合方式并为二次开发提供基础。1. 成绩管理系统拆解JSPServlet 还能打但你要知道它适合哪类需求很多同学拿到《基于JspSevlet实现的Web成绩管理系统》这类题目时第一反应是这技术是不是过时了。先把结论说清楚标题里的 Sevlet 只是 Servlet 的拼写笔误网上不少题目 PDF 都这么印不用纠结题目编号 100010041 也只是资料库的入库编号跟项目版本没有关系。这套组合做课设、毕设和中小型内部后台是够用的它把 Java 处理请求、JDBC 读写 MySQL、JSP 渲染页面这条经典链路一整套走完尤其适合需要现场演示、能讲清楚 MVC 的交付场景。系统的核心需求是成绩录入、查询、统计和 Web 端展示学习者要掌握的是请求流转顺序和数据在 JSP 与 Servlet 之间怎么传而不是去啃复杂框架。下面按架构、建表、代码、踩坑、进阶五层讲新手能照着复现熟手也能找到参数边界。2. 为什么 JSPServlet 做成绩管理依然够用架构与项目骨架2.1 MVC 拆解JSP、Servlet、JavaBean 各管哪一块如果只盯着页面看你会觉得这套技术很绕JSP 里既有 HTML 又有 Java 代码Servlet 里又全是 Java。很多同学乱放业务逻辑JSP 里写一大段% %直接查数据库Servlet 只做转发最后项目变成一团乱麻。我见到最多的成绩管理系统翻车不是功能凑不齐而是视图和控制层混在一个文件里后面加一个需求要改三四处地方。正确做法是把职责拆开各管一段层用的技术职责视图 ViewJSP JSTL渲染 HTML 表单和表格用 EL 表达式取数据控制器 ControllerServlet接收请求、解析参数、调业务、控制页面跳转模型 ModelJavaBean / DAO实体字段、JDBC 数据访问、成绩统计逻辑一个典型的请求流转是这样的浏览器打开登录页填写用户名密码提交到 LoginServletServlet 调 DAO 查数据库验证成功后把用户对象放进 session然后重定向到成绩列表页列表页的 ListServlet 再从数据库查出成绩集合放入 request 属性forward 到 score_list.jsp 渲染。整个过程里JSP 不碰数据库Servlet 不写 HTMLJavaBean 不处理页面跳转。这里说的 JavaBean 在今天更多叫 POJO就是只有私有字段和 getter/setter 的普通 Java 类比如 Student.java、Course.java、Score.java。有些资料会让你用BaseServlet配合methodaddmethoddelete这种一个 Servlet 处理多个动作的写法。做一个成绩管理系统我不推荐理由很实际评委或老师问起来你很难三句话讲清楚一个 Servlet 里五个分支的跳转逻辑而且一旦某个 action 参数拼错整个 Servlet 都 500。资源少、动作简单时一个 Servlet 对应一个资源路径是最好讲、最好改的方案后面第 4 章会给出完整写法。2.2 IDEA 2024 创建 web 项目的骨架目录约定与 web.xml不管你是用 IDEA 2024 创建 web 项目还是用老版本Java Web 的目录规范都由 Servlet 容器说了算IDE 只是生成骨架的工具。常见做法是创建一个 Maven Web 项目结构类似下面这样score-system/ ├── pom.xml ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ ├── com/grade/controller/ # Servlet │ │ │ ├── com/grade/dao/ # JDBC 数据访问 │ │ │ ├── com/grade/entity/ # POJO │ │ │ ├── com/grade/filter/ # 登录过滤器 │ │ │ └── com/grade/util/ # 连接池工具类 │ │ └── webapp/ │ │ ├── WEB-INF/ │ │ │ ├── web.xml │ │ │ └── lib/ # 手动拷贝 mysql、jstl 等 jar │ │ ├── static/ # css、js、图片 │ │ ├── jsp/ │ │ │ ├── login.jsp │ │ │ ├── score_list.jsp │ │ │ └── student_list.jsp │ │ └── index.jsp如果不用 Maven也可以直接在 IDEA 新建普通 Java 项目然后把依赖 jar 拷贝到webapp/WEB-INF/lib目录效果一样只是后面加 jar 时要手动重复一次。重点提醒WEB-INF目录下的 JSP 不能通过浏览器直接输入 URL 访问必须经过 Servlet 用 forward 转发进去这是 Servlet 规范对受保护页面的限制。很多初学者把 login.jsp 放在WEB-INF下然后用sendRedirect跳转结果 404还以为部署出了问题其实 redirect 是浏览器发起新请求根本抵达不了WEB-INF。骨架搭好之后web.xml里要做的配置并不多最基础的是首页和会话超时?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 display-namescore-system/display-name welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list session-config session-timeout30/session-timeout /session-config /web-appwelcome-file表示访问http://localhost:8080/score/时默认展示 login.jsp这个值要写 webapp 根目下的真实路径别写WEB-INF/jsp/login.jsp。session-timeout单位是分钟设为 30 表示用户 30 分钟不操作就过期这个参数对课设够用对正式系统可以再结合记住登录状态的功能来调。如果你的 Tomcat 在 IDEA 里启动后访问项目名返回 404优先看下一节说的 Artifact 发布方式多半不是 web.xml 写错。3. 数据库设计与成绩管理核心表结构3.1 学生-课程-成绩三张表字段选型与唯一约束成绩管理系统的数据模型很固定就是“学生-课程-成绩”三个核心实体。千万不要设计成一张大宽表把学生姓名、课程名、成绩全塞进去虽然查询时省一次 JOIN但数据冗余和更新异常会让你后面哭。规范做法是三张表外加外键把成绩表和学生表、课程表关联起来。CREATE TABLE student ( id INT NOT NULL AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, class_name VARCHAR(100) COMMENT 班级, PRIMARY KEY (id), UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表; CREATE TABLE course ( id INT NOT NULL AUTO_INCREMENT, course_no VARCHAR(20) NOT NULL COMMENT 课程编号, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, credit DECIMAL(4,1) DEFAULT 0 COMMENT 学分如 3.5, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表; CREATE TABLE score ( id INT NOT NULL AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, score DECIMAL(5,1) NOT NULL COMMENT 成绩最高可到999.9, exam_date DATE NULL COMMENT 考试日期, PRIMARY KEY (id), UNIQUE KEY uk_stu_course (student_id, course_id), KEY idx_course (course_id), CONSTRAINT fk_score_stu FOREIGN KEY (student_id) REFERENCES student (id), CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT成绩表;这里几个字段选择和参数值得说明。成绩字段用DECIMAL(5,1)而不是float或double因为浮点数存储 89.5 可能出现 89.49998 这类误差排序和展示都会出问题DECIMAL 精确到一位小数满足百分制甚至 150 分制都够。唯一键uk_stu_course (student_id, course_id)是最重要的一道兜底它保证同一个学生对同一门课只能有一条成绩记录即使代码里忘了先查重数据库层面也会把重复插入直接挡掉。外键是另一个要提前想清楚的决策。加了外键删除学生时要先删除成绩否则 MySQL 会报外键约束错误这在课设演示时反而是好事因为能让别人看到你有约束意识。但如果你想做“删除学生连带删除成绩”的级联操作需要在外键上写ON DELETE CASCADE。常见做法是成绩表的外键不写级联业务代码里先删成绩再删学生逻辑更直观也避免误删一条学生记录直接抹掉一堆成绩。另外由于成绩表经常按课程查idx_course这个普通索引也建议保留不是每个外键都会自动建覆盖查询所需的索引。3.2 JDBC 连接池从 DriverManager 到 BasicDataSource老项目里最常见的写法是Class.forName(com.mysql.jdbc.Driver)然后每次访问都DriverManager.getConnection(url, user, password)。这种直连方式确实能跑但成绩管理系统在期末高峰会被几十个人同时查成绩直连模式频繁创建和销毁连接MySQL 一会儿就报Too many connections。我一般会把项目改成连接池推荐 Apache Commons DBCP2配置统一放在db.properties里代码里只保留一个获取连接的工具类。import org.apache.commons.dbcp2.BasicDataSource; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; public class DbPool { private static final BasicDataSource ds new BasicDataSource(); static { try (InputStream in DbPool.class.getClassLoader() .getResourceAsStream(db.properties)) { Properties p new Properties(); p.load(in); ds.setDriverClassName(p.getProperty(jdbc.driver)); ds.setUrl(p.getProperty(jdbc.url)); ds.setUsername(p.getProperty(jdbc.username)); ds.setPassword(p.getProperty(jdbc.password)); ds.setInitialSize(Integer.parseInt(p.getProperty(pool.initialSize, 5))); ds.setMaxTotal(Integer.parseInt(p.getProperty(pool.maxTotal, 20))); ds.setMaxIdle(Integer.parseInt(p.getProperty(pool.maxIdle, 10))); ds.setMaxWaitMillis(Long.parseLong(p.getProperty(pool.maxWaitMillis, 5000))); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return ds.getConnection(); } }对应的db.properties文件放在src/main/resources下jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/grade_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你的密码 pool.initialSize5 pool.maxTotal20 pool.maxIdle10 pool.maxWaitMillis5000参数不要照抄就完事要理解每个值在干什么。initialSize表示启动时预先建 5 个连接避免第一次请求还要现连数据库maxTotal20是连接数上限防止某个页面被反复刷新时把数据库连接打爆maxIdle10是空闲时最多保留的闲置连接maxWaitMillis5000必须设否则线程在拿不到连接时会无限期阻塞页面卡死看不出原因。characterEncodingutf8解决中文写入乱码serverTimezoneAsia/Shanghai是 MySQL 8 下必须加的参数不加会报时区错误。useSSLfalse只适用于本地开发环境如果你的 MySQL 开了 TLS这里反而会连不上。连接池最隐蔽的坑是“连接泄漏”。拿到 Connection 后没在 finally 里关闭表面上看功能正常但连接池的连接被一点点占满跑到第 20 个并发就全部卡住。JDBC 规范里connection.close()在连接池场景下不是物理断开而是把连接归还给池子所以放心在 finally 里关。配合 try-with-resources 写法代码可以更干净try (Connection conn DbPool.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, studentId); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { // 组装对象 } } }这套写法对所有 DAO 方法通用。每次操作数据库都自己手动关连接到了演示那天才不会因为多刷新几次浏览器而直接把 MySQL 拖死。4. 跑通登录与成绩 CRUDServlet 请求流转与 JSP 渲染4.1 登录拦截写 Filter 而不是每页写重复判断成绩管理系统至少要保护两个资源成绩列表和学生列表未登录用户不能直接通过 URL 访问。初学者最常见的做法是在每个 Servlet 开头复制一段if (session.getAttribute(user) null)判断代码短还好页面多了以后漏写一处就是漏洞。更规范的做法是用 Filter 统一拦截这也是 Web 工程里比较基础的安全控制手段。import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.*; import java.io.IOException; WebFilter(/*) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; String uri req.getRequestURI(); // 放行登录页、登录接口和静态资源否则会死循环 if (uri.endsWith(/login.jsp) || uri.endsWith(/user/login) || uri.contains(/static/)) { chain.doFilter(req, resp); return; } // false 表示没有 session 就返回 null不要在这里创建新 session HttpSession session req.getSession(false); if (session null || session.getAttribute(loginUser) null) { resp.sendRedirect(req.getContextPath() /jsp/login.jsp); return; } chain.doFilter(req, resp); } }最容易被忽略的是第一段放行逻辑。如果不放行登录接口/user/login用户提交登录表单时会被这个过滤器拦下来重定向回登录页形成“登录→跳回登录页→再登录”的死循环。另外放行静态资源也很关键CSS 和 JS 如果被拦页面样式加载不出来JSP 里的图片布局就会错位很多人排查半天以为是路径问题。还有一个小细节用request.getSession(false)而不是getSession()后者在 session 不存在时会创建一个新 session等于一个未登录用户访问任何页面都会白白生成 session 对象增加服务端内存压力。Filter 里的resp.sendRedirect(request.getContextPath() /jsp/login.jsp)必须带getContextPath()。ContextPath 就是部署的项目名比如/score不带它时重定向会指向http://localhost:8080/jsp/login.jsp丢掉项目上下文直接 404。这是 Java Web 里最容易犯的路径错误之一。4.2 成绩录入与列表展示一个 Servlet 只做一件事成绩管理最核心的写入动作是录成绩。一个可以抄作业的写法是按资源建 Servlet避免用methodaddmethoddelete把多个动作堆在同一个类里。比如添加工资就建一个ScoreAddServlet路径映射为/score/add只接收 POST。import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.math.BigDecimal; WebServlet(/score/add) public class ScoreAddServlet extends HttpServlet { private final ScoreDao scoreDao new ScoreDao(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { req.setCharacterEncoding(UTF-8); String studentId req.getParameter(studentId); String courseId req.getParameter(courseId); String scoreStr req.getParameter(score); // 参数校验ID 必须能转成整数成绩必须在 0-100 if (!isNumeric(studentId) || !isNumeric(courseId)) { resp.sendRedirect(req.getContextPath() /jsp/score_list.jsp?errorparam); return; } BigDecimal score new BigDecimal(scoreStr); if (score.compareTo(BigDecimal.ZERO) 0 || score.compareTo(new BigDecimal(100)) 0) { resp.sendRedirect(req.getContextPath() /jsp/score_list.jsp?errorrange); return; } scoreDao.insert(new Score( Integer.parseInt(studentId), Integer.parseInt(courseId), score )); resp.sendRedirect(req.getContextPath() /score/list); } private boolean isNumeric(String s) { if (s null || s.isBlank()) { return false; } try { Integer.parseInt(s); return true; } catch (NumberFormatException e) { return false; } } }doPost而不是doGet原因是成绩写入属于数据变更操作GET 会把参数暴露在 URL 上刷新或收藏时可能触发重复提交。req.setCharacterEncoding(UTF-8)这行一定要在获取任何参数之前调用它的作用是告诉容器用 UTF-8 解码请求体放错位置会失效。写入完成后的sendRedirect也是一种防重复提交手段如果这里用 forward 去跳转列表页用户按 F5 刷新时浏览器会把上一次 POST 请求原样再发一次成绩就被插了两遍改成重定向后刷新时是 GET 请求列表页安全很多。再配合一个只负责查询的ScoreListServlet路径/score/list查完数据用 forward 转发到 JSPWebServlet(/score/list) public class ScoreListServlet extends HttpServlet { private final ScoreDao scoreDao new ScoreDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); ListScoreView list scoreDao.listWithStudentAndCourse(); req.setAttribute(list, list); req.getRequestDispatcher(/jsp/score_list.jsp).forward(req, resp); } }这里查询用 forward 而不是 sendRedirect因为需要把 request 作用域里的list属性带给 JSP如果 sendRedirectrequest 就销毁了属性也带不过去。JSP 端渲染时不要把ScoreView的属性直接用getXxx()写在% %里用 JSTL 遍历更干净% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html body table border1 tr th学号/th th姓名/th th课程/th th成绩/th /tr c:forEach items${list} vars tr tdc:out value${s.studentNo}//td tdc:out value${s.studentName}//td tdc:out value${s.courseName}//td tdc:out value${s.score}//td /tr /c:forEach /table /body /htmlc:out value${s.score}这层不是多此一举c:out会做 HTML 转义。学生姓名、班级这类字段前端可能拿到手写输入直接用${s.studentName}输出如果值里被塞了script浏览器就会执行。成绩管理系统虽然多为内部使用但作为 Web 项目演示考虑一层输出转义能避免被评委问住。EL 表达式里的s.score会调用ScoreView.getScore()属性名要跟 getter 对应否则页面显示空白。5. 避坑JSPServlet 成绩管理系统最常见的四个翻车现场5.1 登录态失效刷新一下就被踢回登录页现象登录成功后进入成绩列表按一下 F5 刷新页面直接跳回 login.jsp反复登录反复跳。原因最常见的有三种。一是登录 Servlet 只把用户对象放进了 request没有放进 session刷新后 request 销毁用户信息就没了二是 Filter 的放行名单漏掉了 CSS、JS 或登录接口本身导致刷新时某个请求被重定向回登录页三是代码里某处调用了session.invalidate()或者设置了过短的 session 超时。解决登录成功后单独写一行把用户放进 session这一行是登录流程里的“定海神针”HttpSession session req.getSession(); session.setAttribute(loginUser, user);然后回到 Filter 中检查放行名单确保/login.jsp、/user/login、/static/全部放行。如果刷新时带着 sessionId 但依然被踢打开浏览器开发者工具看响应头里的Set-Cookie凡是出现了新的JSESSIONID说明服务端认为旧 session 不存在优先检查是不是web.xml里把session-timeout配成了 1 之类的极小值或者代码里创建 session 时报了异常。5.2 中文乱码表单、GET 参数、数据库三层边界不一致现象JSP 页面上中文显示正常提交中文到数据库后变成问号或者 POST 提交正常但 GET 方式传中文参数乱码。原因中文乱码是三个环节没有对齐。第一JSP 页面本身的编码没有声明为 UTF-8第二Servlet 读取 POST 请求参数时没有指定req.setCharacterEncoding(UTF-8)第三数据库连接 URL 没带characterEncodingutf8或者表本身建在了latin1字符集下。解决三层各查一次第一层在 JSP 最顶部写全 page 指令里的contentType和pageEncoding同时 html 的 meta 也写charsetUTF-8。第二层用过滤器统一下编码不要在每个 Servlet 里单独写WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); resp.setContentType(text/html; charsetUTF-8); chain.doFilter(req, resp); } }第三层检查 JDBC URL确保characterEncodingutf8存在并确认表本身是 utf8mb4。如果建表时没指定对已存在的库执行ALTER TABLE student CONVERT TO CHARACTER SET utf8mb4;能救回来。GET 参数的乱码还有一个独立原因Tomcat 8 及以上版本对 URL 默认用 UTF-8 解码但如果你的 Tomcat 被改过URIEncoding或者你用的是更老版本就要在server.xml的连接器上补URIEncodingUTF-8。这个位置很隐蔽排查时容易漏。5.3 数据库连不上驱动、URL 参数与 lib 缺一不可现象Tomcat 启动时不报错第一次访问成绩列表页面时却抛ClassNotFoundException: com.mysql.cj.jdbc.Driver或者Public Key Retrieval is not allowed。原因前者是驱动 jar 没有到达运行时 classpath最常见的情形是你只在 IDEA 的项目依赖里加了 mysql-connector-j但没有复制到webapp/WEB-INF/lib或者没有配置 Maven 的war打包方式。后者是 MySQL 8 默认使用caching_sha2_password认证插件驱动在非 SSL 连接下拿不到 RSA 公钥所以拒绝连接。解决第一步检查WEB-INF/lib下有没有 mysql 驱动 jar没有就手动拷一份进去并重新构建 Artifact用 Maven 的话检查scope是不是runtime同时确认maven-war-plugin正常启用。第二步把驱动类名改成新版驱动对应的com.mysql.cj.jdbc.Driver旧类名com.mysql.jdbc.Driver在 MySQL Connector/J 8.x 里已经标记为过时。第三步在 JDBC URL 上追加jdbc.urljdbc:mysql://localhost:3306/grade_db?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaicharacterEncodingutf8从 Web 服务器安全这个角度看还有一个不得不提的习惯不要把数据库账号密码硬编码在 Java 源码里。课设做完提交源码时代码里写着root/123456的仓库一旦被扫到等于把你的数据库直接暴露了。至少要把密码放到db.properties并在.gitignore里排除掉演示时再单独加载配置这比用一套复杂的加密机制更实际。5.4 404 和旧 class 加载部署路径是隐藏坑现象Tomcat 启动日志正常浏览器访问项目名却 404改动 Java 代码后重启 Tomcat页面还是旧行为像被缓存了一样。原因这两个问题本质上是 Artifact 发布路径不对。IDEA 集成 Tomcat 时如果没把项目按 exploded war 方式部署到 Tomcat 的运行目录而是把一个战包塞到了错误的 webapps 路径页面自然找不到。另一个因素是target/out目录下留有旧 class 文件Tomcat 又优先加载了旧 class导致你怎么改 Java 都不生效。解决在 IDEA 的 Tomcat 配置里部署页签选择Artifact并确认展示的是xxx_war_exploded而不是xxx_war包形式点击右键选择部署到 Tomcat保证项目被解压到运行目录下。修改代码后执行一次Build - Rebuild Project把 Tomcat 的work/Catalina缓存清掉再重启。如果还不行直接删除target和out目录重新构建。这类“改代码没反应”的玄学问题十有八九是旧 class 没被清掉跟代码本身没有关系。6. 让成绩管理系统更耐打分页查询与按需封装成绩数据量一大全表查询就会变得很难看。一门课程 500 人按学期累计就上千行列表页一次性渲染会拖慢 Tomcat 响应速度。给这个系统加一个分页是最能体现“工程思维”的进阶改造。分页 SQL 很简单用 LIMIT 把结果限制在固定窗口内SELECT s.student_no, s.name AS student_name, c.course_name, sc.score FROM score sc JOIN student s ON sc.student_id s.id JOIN course c ON sc.course_id c.id ORDER BY sc.id LIMIT ?, ?;第一个问号是偏移量 offset第二个是每页条数 pageSize。Servlet 里按页码计算偏移量int page 1; int pageSize 10; try { page Integer.parseInt(req.getParameter(page)); } catch (NumberFormatException e) { page 1; // 参数非法时回到第一页 } if (page 1) { page 1; } int offset (page - 1) * pageSize; ListScoreView list scoreDao.listPage(offset, pageSize); int total scoreDao.count(); int totalPages (total pageSize - 1) / pageSize; req.setAttribute(list, list); req.setAttribute(page, page); req.setAttribute(totalPages, totalPages); req.getRequestDispatcher(/jsp/score_list.jsp).forward(req, resp);这段代码有三个可以照抄的细节第一Integer.parseInt包一层 try-catch否则用户传pageabc会让整个页面 500第二page 1时强制回 1防止负数和零造成偏移量错误第三totalPages用(total pageSize - 1) / pageSize向上取整避免总分页数比实际少一页。分页排序字段不能随便写ORDER BY sc.id要选有唯一性的字段否则同分数据在翻页时可能重复出现。往上走一层我一般会把分页结果封装成一个通用的PageResultT类把list、page、totalPages三个属性绑定到一个对象里Controller 里只setAttribute(pageResult, pageResult)JSP 里统一点取。这样一个改造在答辩时可以明确讲出“我对查询层做了分页封装避免大数据量全表加载”比单纯演示功能更有印象分。我自己带这个项目时最血泪的一次是演示前清 Tomcat 缓存不够彻底JSP 改了三次没生效白白给评委讲了三遍旧逻辑。从那以后我就给自己定了规矩每次改完 Java 代码先 Rebuild 再启动每次动数据库字段先备份再重建每次部署前看一眼WEB-INF/lib和target目录。系统可以小工程习惯不能小。希望帮到你。本文还有配套的精品资源点击获取
返回列表