基于Servlet+JSP+MySQL的影视管理系统课程设计全解析

基于Servlet+JSP+MySQL的影视管理系统课程设计全解析 简介基于Servlet、JSP与MySQL的影视管理系统Java Web课程设计项目完整覆盖从需求分析、MVC分层设计到前后端实现与测试的开发流程。系统包含用户注册登录、电影信息查询、评论管理与排行榜等典型功能适合Java Web初学者、课程设计学生及需要项目参考的开发者。压缩包共112个文件除核心Java源码与JSP页面外还提供SQL建表脚本、CSS样式、JS交互脚本、Jar依赖库及XML配置总大小10.03MB目录结构清晰可直接导入IDE运行。已有132人学习下载。通过该项目可深入理解Servlet处理机制、JSP渲染逻辑与JDBC数据库操作的完整链路并在现有代码基础上快速二次改造是巩固Java Web实践能力的优质案例。1. 基于servletjspmysql的影视管理系统结课答辩前你只需要盯住这三层课程设计里“基于servletjspmysql的影视管理系统.zip”这类题目出现频率极高它不追求技术新而是有意把 Java Web 最原始的链路——浏览器发请求、Servlet 接参数、JSP 渲染页面、MySQL 落库——全部暴露在明面上。正因为没有框架替你托管真正理解 Tomcat 什么时候编译 JSP、连接串里每个参数为什么存在这个项目才能顺利跑通而不是靠复制一份现成代码蒙混过关。这个系统适合两类人一类是拿到课程设计题目后需要几天内交付可演示版本的学生另一类是想借小型业务把 Servlet 生命周期、JDBC、会话管理串起来讲的初级开发者。实现思路并不复杂难的是把 Tomcat 面对 JSP 时的编译机制、MySQL 8.0 的驱动与认证差异、请求转发时参数作用域这几个课堂不讲的部分补齐。这篇内容就按“理论 → 实现 → 部署排错 → 答辩加分”的顺序把你手工搭这套系统时最常见的动作和报错一次性讲完。2. 课程设计里的 MVC 分层servlet 管流程jsp 只渲染mysql 承接持久化2.1 为什么是三件套而不是 Spring Boot很多人在动手前会问课程设计为什么还要用 servletjspmysqlSpring Boot 用三行注解就能起一个 Web 服务谁还愿意手写 HttpServlet但框架把细节藏起来之后报错的解释成本反而更高。你配一个 DispatcherServlet就要面对 DispatcherServlet、webApplicationContext 和 rootWebApplicationContext 这类父子容器概念而在三件套里Servlet 从哪里来、走到哪里去代码里写得明明白白课堂上一个请求的生命周期演示就能完整覆盖。影视管理系统本质是增删改查核心业务一个 MovieServlet 就能吃下JSP 承担页面展示MySQL 只需三张表左右。这个技术栈的代码量远小于引入 Spring Boot而且它对 IDE 版本、JDK 版本、网络环境都不挑能减少答辩现场的环境偶发问题。从学习维度看做完这个项目你能回答“JSP 第一次请求为什么慢”“PreparedStatement 和 Statement 区别”“session 为什么依赖 Cookie”这类问题它们比框架注解更常出现在面试里。2.2 项目目录结构与 WEB-INF 的分工课程设计项目的目录结构直接决定了部署时能不能被 Tomcat 正常识别。Eclipse 里 Dynamic Web Project 的源码目录叫 WebContentIDEA 项目则叫 webapp不管哪种内部都要遵守同一个约定WEB-INF 目录下保存 web.xml、lib、classes外部浏览器访问不到只有服务器内部能读取。一个典型的影视管理系统可以这样组织webapp/ ├── index.jsp ├── WEB-INF/ │ ├── web.xml │ ├── lib/ │ │ ├── mysql-connector-java-8.0.33.jar │ │ └── jstl-1.2.jar │ └── jsp/ │ ├── login.jsp │ ├── movie_list.jsp │ └── movie_edit.jsp ├── static/ │ ├── css/ │ └── js/ └── upload/ # 海报等上传文件的落盘目录 src/main/java/ └── com/example/movie/ ├── entity/Movie.java ├── dao/MovieDao.java ├── servlet/ │ ├── MovieListServlet.java │ ├── MovieSaveServlet.java │ └── MovieDeleteServlet.java └── util/DBUtil.java把 JSP 放到 WEB-INF/jsp 而不是根目录是为了不让用户直接输入 URL 访问页面所有页面都通过 Servlet 跳转进入这样登录拦截才能生效。lib 目录放 mysql 驱动、JSTL 等 jar 包其中 mysql-connector-java 要选和数据库版本匹配的MySQL 8.0 对应驱动类 com.mysql.cj.jdbc.Driver5.7 及更旧版本用 com.mysql.jdbc.Driver。classes 目录放着 Servlet 编译后的 class 文件由 IDE 自动输出不要手动改。upload 目录用于封面图落盘需要让 Tomcat 进程对它拥有读写权限否则保存图片时会直接抛 IOException。2.3 web.xml 注册 Servlet还是直接用 WebServletServlet 3.0Tomcat 7 及以上开始支持 WebServlet 注解可以完全不写 web.xml。课程设计我建议保留 web.xml理由很直接老师打开文件就能看到哪些路径对应哪个 Servlet不用逐个类去翻注解而且老版本 IDE 和 Tomcat 的兼容性更好。如果项目里还要加 Filter 和 Listener集中写在 web.xml 里也更容易交代。下面是 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_3_1.xsd version3.1 display-namemovie-admin/display-name welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list servlet servlet-nameMovieListServlet/servlet-name servlet-classcom.example.movie.servlet.MovieListServlet/servlet-class /servlet servlet-mapping servlet-nameMovieListServlet/servlet-name url-pattern/movie/list/url-pattern /servlet-mapping session-config session-timeout30/session-timeout /session-config /web-app上面这段里servlet-name 只是内部标识真正决定访问路径的是 servlet-mapping 中的 url-pattern。这里配置为 /movie/list浏览器访问地址就是 http://localhost:8080/项目上下文/movie/list。有人把 url-pattern 写成 /movie/list/导致带子路径的请求也进入同一个 Servlet做权限校验时容易出现 uri 判断失误课程设计只写精确匹配就够了。url-pattern 一共有三类写法精确匹配/movie/list、路径匹配/movie/、扩展匹配*.do它们之间有优先级但小型项目不需要全部掌握。两种注册方式对比如下对比点web.xml 注册WebServlet 注解适用 Servlet 版本2.5 及以前必须3.0url-pattern 可见性集中管理一眼看全分散在各实体类上老 IDE 兼容性更好需要项目 facets 正确课程设计演示方便现场改路径改完必须重新编译2.4 一个 Servlet 要具备的最小结构Servlet 的生命周期由 Tomcat 管理实例化、init、service、destroy。平时你只需要重写 doGet 或 doPostservice 方法会在内部根据 HTTP 方法自动转发到对应的方法。以影视列表为例最简单的 List Servlet 长这样WebServlet(/movie/list) // 注解式映射与 web.xml 二选一 public class MovieListServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 必须在第一次读取参数之前调用否则POST请求体已按默认编码解码 req.setCharacterEncoding(UTF-8); // 指定响应类型和字符集缺了charset中文会乱码 resp.setContentType(text/html;charsetUTF-8); String keyword req.getParameter(keyword); // 从 ?keyword关键字 取值 MovieDao dao new MovieDao(); ListMovie movies dao.searchByName(keyword); // DAO层负责拼SQL req.setAttribute(movies, movies); // 塞进request作用域 req.getRequestDispatcher(/WEB-INF/jsp/movie_list.jsp) .forward(req, resp); // 服务器内部转发 } }这里有两个关键动作。req.setAttribute 把查询结果放进 request 作用域JSP 能用 ${movies} 直接取到getRequestDispatcher().forward() 是服务器内部转发地址栏不变request 里的属性在 JSP 中依然有效。如果这里误用 sendRedirect浏览器会发起第二次全新请求request 属性全部丢失JSP 里拿到的就是 null。WEB-INF 目录是受保护的浏览器直接访问 http://localhost:8080/项目/WEB-INF/jsp/movie_list.jsp 会得到 404只能通过 Servlet 转发进入这本身就构成了一层页面访问控制。3. 影视管理系统的核心链路movie 表、JDBC 查询与 jsp 列表页的串接3.1 建库建表字段长度和索引是答辩里能细讲的地方影视管理系统最好拆成两张表movie 与 category。category 存电影分类movie 通过外键关联它避免在 movie 表里重复存“动作”“科幻”这类字符串这是课程设计评审时最容易看到的规范点之一。建表 SQL 可以直接在 MySQL Workbench 或 Navicat 里执行CREATE DATABASE IF NOT EXISTS movie_db DEFAULT CHARACTER SET utf8mb4; USE movie_db; CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE COMMENT 分类名: 动作/科幻/爱情 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE movie ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL COMMENT 片名, director VARCHAR(50) COMMENT 导演, category_id INT, release_year YEAR, rating DECIMAL(2,1) COMMENT 评分, 精确到0.1, poster VARCHAR(255) COMMENT 海报图片URL或相对路径, description TEXT, created_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_movie_category FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE SET NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; ALTER TABLE movie ADD INDEX idx_title (title); ALTER TABLE movie ADD INDEX idx_rating (rating);字段说明是答辩时可以直接念出来的内容字段类型说明titleVARCHAR(100)片名模糊查询的主字段ratingDECIMAL(2,1)评分范围 0.010.0精确到一位小数release_yearYEAR用 YEAR 类型比 VARCHAR 省空间排序也正确category_idINT外键关联 category 表的 idposterVARCHAR(255)封面图存相对路径方便整个项目迁移目录字符集选择 utf8mb4 而不是 utf8是因为 utf8mb4 能完整存储 emoji 和生僻字MySQL 8.0 默认就是 utf8mb4。外键 ON DELETE SET NULL 的含义是删除某个分类后属于该分类的电影保留只是 category_id 被置空这比 ON DELETE CASCADE 更符合“误删分类不应该连电影一起删”的直觉。两个 ALTER TABLE 语句给 title 和 rating 建了索引虽然课程设计数据量很小、索引效果不明显但答辩时可以说“搜索片名和按评分排序的语句会走二级索引避免全表扫描”这句话比单纯展示堆代码有用得多。3.2 JDBC 连接串参数少了 serverTimezone 页面就会白等JDBC 连接串是课程设计里翻车率最高的位置报错常见的有三类ClassNotFoundException、Public Key Retrieval is not allowed、Server returns invalid timezone。这些问题都能通过一个正确的连接串和驱动类提前规避。写一个专门的 DBUtil 来管理连接是最简洁的做法public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/movie_db ?useUnicodetruecharacterEncodingUTF-8 serverTimezoneAsia/ShanghaiuseSSLfalse allowPublicKeyRetrievaltrue; private static final String USER root; private static final String PASSWORD 123456; static { try { // 注意: 驱动8.0用com.mysql.cj.jdbc.Driver Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(没找到mysql驱动, 检查WEB-INF/lib); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }连接串里每个参数都有明确作用参数作用缺失时的典型表现useUnicodetruecharacterEncodingUTF-8客户端与数据库之间按 UTF-8 传输字符中文乱码serverTimezoneAsia/Shanghai指定服务器时区报 serverTimezone 相关连接异常useSSLfalse本地开发不启用 TLS 握手连接卡顿或 SSL 握手告警allowPublicKeyRetrievaltrue允许客户端获取 RSA 公钥Public Key Retrieval is not allowedallowPublicKeyRetrieval 是 MySQL 8.0 后才常见的问题8.0 默认认证插件是 caching_sha2_password首次连接需要服务端公钥做加密认证驱动默认不允许自动获取公钥于是直接拒绝。如果你的数据库是 5.7把驱动换成 mysql-connector-java 5.1.x连接串里去掉这个参数、驱动类改回 com.mysql.jdbc.Driver 就够用。本机如果不想一步步安装 MySQL也可以直接用 docker 拉起一个 8.0 实例映射 3306 端口后连接串不变能省掉一些环境变量配置上的时间。3.3 模糊查询与 PreparedStatement 参数LIKE 拼错才最让人头疼搜索是影视管理系统的核心交互用户输入片名或导演名后台做模糊匹配。这里推荐用 PreparedStatement 而不是直接拼字符串一方面防止 SQL 注入另一方面参数用问号占位代码更干净public ListMovie searchByName(String keyword) { String sql SELECT m.id, m.title, m.director, m.release_year, m.rating, m.description, c.name AS category_name FROM movie m LEFT JOIN category c ON m.category_id c.id WHERE m.title LIKE ? OR m.director LIKE ? ORDER BY m.rating DESC; ListMovie list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { String like % (keyword null ? : keyword.trim()) %; ps.setString(1, like); ps.setString(2, like); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Movie m new Movie(); m.setId(rs.getInt(id)); m.setTitle(rs.getString(title)); m.setRating(rs.getDouble(rating)); list.add(m); } } } catch (SQLException e) { e.printStackTrace(); // 课程设计阶段先打印, 交付前换成日志方式 } return list; }这段代码里有两个需要留神的细节。第一个是 LIKE 参数的位置SQL 里有两个问号分别对应 title 和 director所以 ps.setString 要按顺序调用两次顺序颠倒或漏掉一个会直接抛 Parameter index out of range。第二个是 keyword 为 null 的情况req.getParameter 在参数不存在时返回 nullString.trim() 会抛 NullPointerException所以先判空再拼接 %%。LEFT JOIN 的选择也有讲究如果某部电影还没有分类INNER JOIN 会把整行过滤掉LEFT JOIN 则保留电影行category_name 显示 null页面里用 JSTL 判断空值即可。try-with-resources 写法保证 Connection、PreparedStatement、ResultSet 在方法结束时自动关闭避免连接泄漏这个细节在答辩时可以直接说“所有 JDBC 资源都在 try-with-resources 内关闭防止数据库连接耗尽”。3.4 jsp 列表页EL 表达式与 JSTL 的配合查询结果通过 request.setAttribute(movies, movies) 传到 JSP 后页面用 EL 表达式和 JSTL 标签来渲染表格。一个完整的影视列表页可以这样写% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html body h2影视列表/h2 form action${pageContext.request.contextPath}/movie/list methodget 片名: input typetext namekeyword value${param.keyword} input typesubmit value搜索 /form table border1 trthID/thth片名/thth导演/thth评分/thth操作/th/tr c:forEach items${movies} varm tr td${m.id}/td td${m.title}/td td${m.director}/td td${m.rating}/td td a href${pageContext.request.contextPath}/movie/edit?id${m.id}编辑/a a href${pageContext.request.contextPath}/movie/delete?id${m.id}删除/a /td /tr /c:forEach /table /body /html这里最容易踩坑的是 ${m.title} 到底取什么EL 表达式会调用 Movie 类的 getTitle() 方法也就是必须写标准的 getter/setter如果属性叫 title 却没有 getTitle()页面会直接报 PropertyNotFoundException。${param.keyword} 是从查询串里取参数的专用写法相当于 request.getParameter(keyword)用来在刷新后回显搜索关键字。action 和链接里都写了 ${pageContext.request.contextPath}这是获取当前应用上下文路径的动态方式如果项目部署到 Tomcat 时上下文名改成别的链接仍能正确拼接写死 /movie 会导致部署路径一变所有链接全 404。JSTL 的 c:forEach 是循环标签items 是要遍历的集合var 是每次循环的临时变量名循环体里就是一行表格数据。4. Tomcat 部署时 jsp 编译 class、mysql 连接与 404 的排查路径4.1 配置 Tomcat 后 jsp 编译成的 java 类和 class 文件在哪Tomcat 部署后第一次访问某个 JSP 页面会明显卡一下之后再用就变快原因是 JSP 要由 Jasper 引擎编译JSP 文件先被翻译成 Servlet 源码 .java再编译成 .class最后才由容器调用。这正好解释了配置 Tomcat 后查看 JSP 编译后的 java 类这个高频问题。中间产物默认放在 Tomcat 的 work 目录下路径结构是work/Catalina/localhost/应用上下文名/org/apache/jsp/ ├── movie_list_jsp.java └── movie_list_jsp.class在命令行里也可以直接查找# Linux / macOS find /usr/local/tomcat/work -name movie_list_jsp.java 2/dev/null # Windows PowerShell Get-ChildItem -Recurse -Path $env:CATALINA_HOME\work -Filter movie_list_jsp.java打开生成的 movie_list_jsp.java能看到整个 JSP 页面被翻译进 _jspService(HttpServletRequest, HttpServletResponse) 方法里静态 HTML 变成了 out.write() 调用c:forEach 标签则被展开成真实运行的 Java 循环代码。这就是为什么 JSP 里杂糅大量 % % 脚本片断会拖慢页面每个脚本片断都会原样嵌入 Java 方法体请求越多执行负担越重。如果修改 JSP 后刷新页面还是旧内容先看浏览器缓存再看 work 目录对应 .class 文件的修改时间是否真的更新了手动删除整个 work 目录再重启 Tomcat会让所有 JSP 重新编译这是一种强制清理手段。修改 JSP 后的“离开页面提示”那种 onbeforeunload 客户端行为属于浏览器事件跟 Jasper 编译没有关系别混在一起排查。4.2 MySQL 从安装到连接五个高频报错的定位顺序MySQL 环境没就绪时项目启动后所有请求都会卡在数据库操作处。开发机上的 MySQL 可以用免安装版 zip 快速搭建解压后新建 my.ini 指定 basedir 和 datadir管理员命令行执行 mysqld --initialize-insecure 做初始化再用 net start mysql 启动服务最后用 mysql -uroot 连接给 root 设置密码。整个流程里验证服务是否在监听比反复改 Java 代码更有效# 1) 检查 MySQL 进程 tasklist | findstr mysql # 2) 确认 3306 端口在监听 netstat -ano | findstr 3306 # 3) 本机登录并查看目标库 mysql -uroot -p123456 -h127.0.0.1 -P3306 movie_db -e show tables;下面是课程设计里最高频的 MySQL 连接报错按出现频率排好了定位顺序报错现象常见原因定位方式ClassNotFoundException: com.mysql.cj.jdbc.DriverWEB-INF/lib 下缺驱动 jar检查 jar 是否在 lib 目录重启 TomcatAccess denied for user rootlocalhost密码错误或认证插件不匹配mysql -uroot -p 本机登录验证Unknown database movie_db库没创建或连接串库名拼错show databases;Communications link failureMySQL 服务没启动 / 端口被改netstat 确认 3306 监听Public Key Retrieval is not allowedMySQL 8.0 默认认证插件问题连接串加 allowPublicKeyRetrievaltrue提示数据库连接问题不要先怀疑 Java 代码。先用 MySQL 客户端工具Workbench 或 Navicat在外部连一次同一地址和账号能连上就说明问题在项目侧连不上就把精力放在 MySQL 服务、端口和密码本身。4.3 404、500 与“找不到主类”的排查顺序Servlet 项目部署后最常见的错误并不是代码崩溃而是 404。排查顺序应该是先确认应用上下文也就是 URL 里的第一段路径。项目部署在 Tomcat webapps 下产生一个名为 movie 的目录时访问地址是 http://localhost:8080/movie/movie/list如果打成 ROOT.war 部署地址才是 http://localhost:8080/movie/list。上下文不一致所有的链接都会 404。第一层级看 URL 与 web.xml 或 WebServlet 里的 url-pattern 是否一一对应。把浏览器地址换成直接访问 Servlet 本身例如 /movie/list如果这个地址也 404说明映射有问题如果 Servlet 能访问但页面报 404说明转发目标 JSP 的路径不对重点检查是不是写成直接访问 WEB-INF 下文件。第二层级是 500Tomcat 错误页会给出 ServletException 和完整堆栈注意最底部的 Caused byNullPointerException 通常是没查数据库就取对象属性SQLException 就回到 4.2 的表格里对号入座。第三类是 IDE 里常见的“错误: 找不到或无法加载主类”它发生在没有 main 方法的 Servlet 工程里原因是把 Web 项目当作普通 Java Application 运行了。Eclipse 里右键选择 Run As - Run on ServerIDEA 里先配置 Tomcat Server 再以 war exploded 方式部署让容器自己加载类和 lib而不是让 JVM 找一个不存在的入口类。5. 让影视管理系统从“能跑”到“能讲”过滤器、连接池与答辩加分点5.1 用 Filter 统一登录拦截与编码设置系统一旦加入后台管理就需要登录校验。在每个 Servlet 里都写一遍 session 判断是课程设计最常见的冗余代码正确做法是用 Filter 统一拦截后台路径。Filter 在请求到达 Servlet 之前执行多个 Filter 按照 web.xml 里的注册顺序组成过滤链。下面这个过滤器统一处理了编码和登录判断WebFilter(urlPatterns {/movie/*, /admin/*}) public class AuthFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; req.setCharacterEncoding(UTF-8); // 全局编码, 替代每个Servlet中的重复写法 HttpSession session req.getSession(false); boolean needAuth !req.getRequestURI().endsWith(/movie/list); if (needAuth (session null || session.getAttribute(loginUser) null)) { ((HttpServletResponse) response) .sendRedirect(req.getContextPath() /login.jsp); return; } chain.doFilter(request, response); // 放行到目标Servlet } }这里有两个易错点。getSession(false) 表示不主动创建 session如果用户未登录就访问后台这个方法返回 null配合后面的空判断才能正确跳转如果写成 getSession(true)每个未登录请求都会被创建一个新的 HttpSession白白占用内存。过滤链的最后必须执行 chain.doFilter否则请求会在过滤器中断页面表现为一直空白。拦截路径用注解里的 urlPatterns 声明注意别把 login.jsp 和静态资源css/js也拦进来否则登录页自己都会被重定向到登录页形成循环跳转。5.2 连接池与事务老师问“性能”时怎么答课程设计阶段用 DriverManager 每次 getConnection 完全够用但答辩时如果被问到“并发请求多了怎么办”连接池就是最好的回答素材。常见做法是用 Druid 或 HikariCP 替换裸 JDBC把 DBUtil 里的 DriverManager.getConnection 换成从池中获取连接DruidDataSource dataSource new DruidDataSource(); dataSource.setUrl(URL); dataSource.setUsername(root); dataSource.setPassword(123456); dataSource.setInitialSize(5); // 启动时建立5个连接 dataSource.setMaxActive(20); // 连接池最大连接数 dataSource.setMaxWait(60000); // 拿不到连接的等待时间, 单位毫秒 Connection conn dataSource.getConnection();注意使用连接池后代码里的 conn.close() 不再是真正的关闭连接而是把连接归还给池子这一点可以直接讲给老师听。下面对比两种方式对比项DriverManager.getConnectionDruid/HikariCP 连接池连接获取每次请求新建一条 TCP 连接从池中复用空闲连接并发 100 次请求连接数线性增长延迟明显稳定在 initialSize 到 maxActive额外依赖只有 mysql 驱动druid jar slf4j-api jar课程设计工作量零配置多 3-5 行配置事务方面如果影视管理里有“删除分类时同步更新电影列表”“登记评价后刷新评分”这类跨表操作就值得在 Save Servlet 里用 Connection.setAutoCommit(false)业务逻辑结束后 commit任一步异常就 rollback。MySQL 的存储过程在这个项目里通常不是必须但如果老师追问数据库能力可以顺手提“也可以用存储过程封装统计电影数量的逻辑用 CallableStatement 调用”。5.3 三个能写进答辩稿的具体细节最后整理三个成本低、效果好的加分项可以直接写进演示稿或答辩陈述里加分点实现位置一句话讲解口径索引movie 表的 title、rating 列搜索和评分排行的 SQL 走二级索引避免全表扫描事务MovieSaveServlet 里操作多张表setAutoCommit(false) 保证数据一致性异常时 rollback连接池DBUtil 改为 Druid 数据源连接复用替代每次新建支撑更高并发评审老师最在意的是“这个项目是不是你自己写的、能不能讲清楚”。有一个很有效的准备动作在答辩前打开 Tomcat 的 work 目录用 javap -c 反编译访问过的 movie_list_jsp.class在字节码里找到页面中 JSTL 循环对应的位置。当你能够指着反编译结果说“JSP 在第一次访问时被 Jasper 编译成一个 Servlet我的页面循环就在这里展开”时这门课程设计的验收标准就已经完全落在你这一侧了。本文还有配套的精品资源点击获取