news 2026/10/14 15:06:17

Java Web三层架构企业简历管理系统:课设答辩实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web三层架构企业简历管理系统:课设答辩实战解析

简介:一套基于Java Web的企业简历管理系统完整源码包,适合课程设计、毕业设计及Java初学者进阶练习,可直接用于毕业设计答辩演示。压缩包内共162个文件,包含55个java源码、17个jsp页面、10个jar依赖包、2个sql数据库脚本,以及编译后的class文件、图片与配置资源,整体大小2.75MB,目录结构清晰,便于导入IDE后直接运行和二次开发。系统实现用户注册登录、简历创建编辑、简历查看、搜索筛选与权限管理等典型功能,采用JSP+Servlet+Service+Dao分层架构,配合MySQL数据库,直观展示MVC模式在真实项目中的组织方式。通过研读源码与SQL脚本,可以掌握从页面请求到Servlet控制、Service业务处理、Dao数据访问的完整调用链,学习用户表、简历表设计思路及常见权限控制写法。目前已有301人学习下载,是一份轻量完整、贴合教学实战的Java Web项目参考资料。

1. 企业简历管理系统:课设答辩能直接讲的 Java Web 三层架构

做课设最怕的不是写不出代码,而是答辩时被问“你这分层到底分在哪”。这份基于 Java Web 的企业简历管理系统,恰恰把 JSP + Servlet + Service + Dao + MySQL 这条经典技术栈完整串了起来。它不是花架子项目,而是把简历创建、筛选、面试录入、笔试评分都收进了同一个系统里,适合拿来写课程设计或毕业设计的实践案例。我拆完源码后发现一个值得说的点:它比大多数只做增删改查的课设多了“招聘流程闭环”这一层业务逻辑,这在答辩时是很加分的谈资。

2. 三层架构到四个关键 Servlet:代码包里的真实业务脉络

2.1 为什么是 JSP + Servlet 而不是 Spring Boot

拿到这个项目的同学可能会有疑问:现在企业里都 Spring Boot 一把梭了,为什么课设还要用 Servlet?答案很现实:课程大纲考的就是手动实现 MVC。这份资源里没有 Spring、没有 MyBatis,所有请求路径、转发、Dao 层连接管理都是手写的,恰恰能让你把 HTTP 请求从进入 Tomcat 到返回 JSP 页面的完整链路讲清楚。

从代码包里能够看到一组带有Servlet.class后缀的文件,这类文件直接暴露了系统的访问入口。比如RMSResumeSchedulingServlet负责简历调度,RMSInterviewEnteringServlet负责面试信息录入,RMSPaperExamEnteringServlet负责笔试成绩录入,RMSResumeBrowsingServlet负责简历浏览。四个入口对应了招聘流程的四个节点:筛选、面试、笔试、归档。这种按“业务流程”而不是按“数据表”来划分控制器的思路,是区分课设水平和项目经验的关键信号。

MVC 在这个项目里的分工很清晰:

  • View 层是 JSP,负责渲染表单和列表。
  • Controller 层是 Servlet,负责接收请求参数、调用业务服务、决定跳转页面。
  • Model 层由 Service + Dao 组成,前者封装业务规则,后者负责 SQL。
  • MySQL 在最后端存储用户表、简历表、面试轮次表和笔试成绩表。

2.2 Dao 层设计:从 impl 到接口的依赖倒置

代码清单里有ApplicantDaoImpl.class和ScoreDaoImpl.class。这里要注意,它们不是孤立类,一般是某个接口的实现,接口名大概率叫ApplicantDao或ScoreDao。为什么要在课设阶段就引入接口?常见做法是 Service 层只依赖接口,不依赖具体实现类;以后如果要换成 MyBatis 的 Mapper,只需新写一个实现类,Service 层代码一行都不用改。

我一般看一个课设项目是否合格,先看它有没有分Dao接口和DaoImpl实现。如果只有 Impl 没有接口,说明作者没理解解耦。如果接口和 Impl 都有,就可以在答辩时说“用到了依赖倒置原则,业务层不直接依赖数据访问细节”。

打开ApplicantDaoImpl的源码后,核心方法往往围绕三块展开:

  • insertApplicant():新增一份简历主记录。
  • updateApplicant():更新基本信息、联系方式、期望薪资。
  • queryApplicantByCondition():按关键词、学历、工作经验做联合查询。

2.3 业务层封装:RoundDaoImpl 揭示的“轮次”模型

很多人第一次看到RoundDaoImpl会愣住:简历系统为什么要管“轮次”?这其实是一个面试安排的多轮记录表。

应届生求职流程中,一个候选人可能要经历一面、二面、HR 面。RoundDao大概率的表结构是:

  • round_id:轮次主键。
  • applicant_id:关联简历主表。
  • round_type:一面、二面、笔试。
  • interviewer:面试官。
  • round_result:通过 / 淘汰 / 待定。
  • score:笔试或面试分数。
  • remark:评价备注。

这个设计的价值在于:同一候选人可以对应多条轮次记录,业务层通过查询该申请者下最大的round_id来判断当前处于第几轮。理解了这张表的含义,去读RoundDaoImpl里的 SQL 就会很快,基本都是SELECT ... WHERE applicant_id = ? ORDER BY round_id这类操作。

2.4 启动过程:RMSInitServlet 与 RMSNavigationServlet 的分工

项目里有两个容易被忽略的 Servlet,一个是RMSInitServlet,一个是RMSNavigationServlet。前者大概率做了初始化操作,比如加载系统配置、建立数据库连接池或写入默认管理员账号。后者通常是一个统一的导航分发器,根据请求参数决定页面跳转方向。

这里有个很实用的架构细节:与其在每个 Servlet 里重复写request.getRequestDispatcher("/xxx.jsp").forward(request, response),不如抽一个导航 Servlet 来统一处理。这虽然在 Servlet 规范里不算强制要求,但可以在答辩中体现“消除重复代码”的意识。它的典型工作过程是:

  • 接收page=resumeList之类参数。
  • 判断参数对应的 JSP 路径。
  • 携带request和response对象完成转发。

2.5 六个核心类串起完整招聘闭环

到这里基本可以把资源包里的 8 个类还原成一个完整业务流。

  • 简历浏览:RMSResumeBrowsingServlet把简历列表查出来,翻到 JSP 展示。
  • 简历调度:RMSResumeSchedulingServlet对合格简历做面试排期。
  • 面试录入:RMSInterviewEnteringServlet填入面试官、时间、评价。
  • 笔试录入:RMSPaperExamEnteringServlet填入笔试成绩和排名。
  • 数据对象:MultiInfo作为复合值对象,承接多表查询的返回结果。
  • 数据访问:ApplicantDaoImpl、ScoreDaoImpl、RoundDaoImpl提供各自的持久化操作。

当我看到MultiInfo这个类名时,第一反应是作者踩过“单表返回不够用”的坑。在简历列表页打出候选人及其最高学历、最近工作单位、面试轮次时,单靠Applicant实体根本装不下,必须弄一个复合对象来装多表 join 的结果。数据库里面这种名字很常见,它就是一个没有业务逻辑的纯数据容器。

3. 把项目跑起来:环境准备、数据库导入和三条起步命令

3.1 环境版本选择:别用 JDK 17 给自己挖坑

这个项目基于 Servlet 3.x 或更早的规范,所以环境搭配非常重要。我建议使用以下组合:

组件推荐版本说明
JDK1.8兼容性最好,不会遇到 module 报错
Tomcat8.5 / 9.0支持 Servlet 3.1,稳定
MySQL5.7 / 8.0若用 8.0 注意驱动包版本
IDEEclipse / IDEA直接导入项目
数据库驱动mysql-connector-java-5.1.495.x 驱动兼容 5.7 和 8.0

很多同学一上来装了最新的 JDK 17,结果 Tomcat 启动直接报UnsupportedClassVersionError,原因就是 class 文件版本号太高。课程设计项目不需要追求新,开箱即跑的稳定才是硬道理。

如果你只有 JDK 11 以上版本,需要在 IDEA 里把 Project Structure 的 Project SDK 和 Java Compiler 都调到一致,并且尽量用 Tomcat 9。Tomcat 9 官方支持 Servlet 4.0,跑这种老项目问题不大,但 Tomcat 10 要谨慎,因为它的 Jakarta EE 命名空间改了,javax.servlet包名全换成了jakarta.servlet,这个项目的源码基于javax.servlet,直接扔到 Tomcat 10 里会编译失败。

3.2 导入数据库:从 SQL 脚本到建库建表

资源包里应该有.sql的数据库脚本,如果你没有找到单独的 SQL 文件,就打开DbUtil或DBConnection类的源码,里面通常写了数据库名、账号密码和初始化表结构。我处理课设项目的习惯是先创建一个专用数据库:

CREATE DATABASE IF NOT EXISTS resume_db DEFAULT CHARACTER SET utf8mb4; USE resume_db; SOURCE /your/path/resume_db.sql;

执行完后,重点检查四张核心表是否建全:user 表(存储账号密码和角色)、applicant/resume 表(简历基本信息)、round 表(面试轮次)、score 表(笔试或评分记录)。如果发现缺少某个字段,可以对照源码中 Dao 层的 SQL 来反推表结构。

这里有一个值得避开的坑:不要用 root 账号直接把密码硬编码在源码里并提交到公开仓库。虽然课设项目无所谓,但老师问到安全问题时容易露怯。比较好的做法是把数据库连接配置抽到一个 properties 文件中,代码里只读取配置。

3.3 IDEA 部署 Tomcat:从 Artifact 到启动验证

以 IntelliJ IDEA 为例,完整部署流程可以按下述步骤操作:

  • 打开 IDEA,选择File -> New -> Project from Existing Sources,选择项目根目录。
  • 选择Import project from external model并勾选Eclipse或直接使用Web目录结构。
  • 配置 Project SDK 为 JDK 1.8。
  • 打开Project Structure -> Facets,添加 Web 模块,指定web.xml路径。
  • 打开Project Structure -> Artifacts,新建Web Application: Exploded。
  • 配置 Tomcat Server,在 Deployment 标签页加入这个 Artifact。
  • 修改 Application context 为/resume,保证访问路径和代码里跳转路径一致。

启动成功后,浏览器访问地址应该是:

http://localhost:8080/resume/login.jsp

如果首页是直接跳转的,启动路径可能是http://localhost:8080/resume/。注意,项目如果用了RMSInitServlet做初始化,第一次启动时它会自动往数据库塞管理员账号,类似admin / admin123,登录前先刷新一下数据库表看有没有插入成功。

3.4 连接 MySQL 8.0 的参数变化

如果你的 MySQL 是 8.0 版本,驱动类和连接串写法需要相应调整。旧代码里常见写法是:

Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/resume_db";

MySQL 8.0 之后的正确写法:

Class.forName("com.mysql.cj.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/resume_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8";

区别在于驱动类名从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver,并且必须加上serverTimezone参数,否则会报时区相关的 SQLException。这是一个非常经典的迁移问题,十个跑 MySQL 8 的课设组里至少有五个会踩到。

在 Dao 层连接方式上,这套代码大概率用的是DriverManager.getConnection()直连方式,资源包里没有看到连接池相关内容。如果答辩想加分,可以改造成 Druid 或 HikariCP 连接池,改动范围集中在 DbUtil 类,大约 30 到 50 行,但对系统稳定性的解释空间会大很多。

4. 手写核心登录与简历管理:安全判断和查询 SQL 的细节

4.1 登录检查:用 Session 控制页面访问权限

虽然这个系统有注册登录功能,但我更关注的是登录状态是否在每个 Servlet 里都做了拦截。一个标准的课设登录校验代码大概是这个样子:

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); User user = userService.login(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("currentUser", user); response.sendRedirect("index.jsp"); } else { request.setAttribute("errorMsg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } }

这段代码里有三个小细节。第一是sendRedirect和forward的选择:登录成功后用重定向,可以避免刷新页面时表单重复提交;登录失败后用转发,可以在同一请求里携带错误信息回显给 JSP。第二是密码存储层面,课设阶段大多数没做 MD5 加密,如果答辩时老师问“密码明文存数据库怎么办”,最好提前把登录模块改成 MD5 加盐校验。第三是权限拦截,很多 Servlet 的 doGet 里没有判断 Session 是否为空,直接访问某个页面会报空指针。可以加一个简单的过滤器(Filter)统一处理,这是体现安全意识的好改动点。

4.2 简历创建的多表写入:事务的边界

简历创建不是只 insert 一张表,至少包含主表、教育经历表、工作经历表和多条技能记录。在一个没有框架的项目里,多表写入最怕的是中途报错导致数据不一致。我一般会在 Service 层用一个事务方法把所有 insert 包起来:

public boolean addResume(Resume resume, List<Education> eduList, List<WorkExperience> workList) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); applicantDao.insertResume(conn, resume); for (Education edu : eduList) { educationDao.insert(conn, edu); } for (WorkExperience work : workList) { workDao.insert(conn, work); } conn.commit(); return true; } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } return false; } finally { DBUtil.closeQuietly(conn); } }

注意上述代码里每个 Dao 方法都接收一个Connection参数,这是手写事务的关键。如果每个 Dao 内部自己getConnection(),那事务就没法跨多个 Dao 共享,后一半插入失败时前面已提交的数据就撤不回来了。这个项目若想把事务写好,务必检查DBUtil是否提供了getCurrentThreadConnection()这类方法,或者直接把 Connection 当作参数逐层传下去。

Dao 层常见的insertResume实现是使用PreparedStatement:

String sql = "INSERT INTO applicant (name, phone, email, education, work_years, expected_salary, create_time) VALUES (?, ?, ?, ?, ?, ?, NOW())"; PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.setString(1, resume.getName()); ps.setString(2, resume.getPhone()); ps.setString(3, resume.getEmail()); ps.setString(4, resume.getEducation()); ps.setInt(5, resume.getWorkYears()); ps.setBigDecimal(6, resume.getExpectedSalary()); ps.executeUpdate(); ResultSet keys = ps.getGeneratedKeys(); if (keys.next()) { resume.setId(keys.getInt(1)); }

RETURN_GENERATED_KEYS这一步特别重要,因为后面插入教育经历表时,需要用简历主键做外键关联。如果漏掉这一步,多表数据就接不上关系。

4.3 简历搜索和筛选:动态拼接 SQL 的注意点

系统的搜索功能支持按关键词、学历、工作经验等条件筛选,这意味着queryApplicantByCondition需要动态拼接 SQL。常见的写法(用StringBuilder)需要注意 SQL 注入:

StringBuilder sql = new StringBuilder("SELECT * FROM applicant WHERE 1=1"); List<String> conditions = new ArrayList<>(); List<Object> params = new ArrayList<>(); if (!StringUtils.isEmpty(keyword)) { conditions.add("(name LIKE ? OR phone LIKE ? OR email LIKE ?)"); params.add("%" + keyword + "%"); params.add("%" + keyword + "%"); params.add("%" + keyword + "%"); } if (education != null) { conditions.add("education = ?"); params.add(education); } if (workYears > 0) { conditions.add("work_years >= ?"); params.add(workYears); } for (String condition : conditions) { sql.append(" AND ").append(condition); } sql.append(" ORDER BY create_time DESC");

两个关键点值得在答辩时讲明白。第一,WHERE 1=1不是多余的拼接技巧,它是为了让后续所有条件都可以统一用AND开头,避免判断“到底是不是第一个条件”的麻烦。第二,所有条件值都用?占位符,参数通过PreparedStatement传入,从机制上杜绝了 SQL 注入。解释清楚这两点,比背十遍 MVC 定义更能证明你懂实际开发。

4.4 把复合查询结果装进 MultiInfo

前面提到MultiInfo这个类,它存在的理由是列表页很可能需要展示:申请人姓名、最高学历、当前状态、最近一轮面试官、最近一次评分。这些数据分布在 applicant、round、score 三张表里,所以 SQL 大概率是这样:

SELECT a.id, a.name, a.education, r.round_result, s.score, r.interviewer FROM applicant a LEFT JOIN round r ON a.id = r.applicant_id LEFT JOIN score s ON a.id = s.applicant_id WHERE a.id = ? ORDER BY r.round_id DESC LIMIT 1

此处LEFT JOIN的使用也值得注意:如果一个人还没进入面试轮次,round表里就没有他的记录,此时INNER JOIN会把这个人直接过滤掉,但简历列表里应该还是要看到他。用LEFT JOIN保证主表数据不丢,这是列表查询的基本习惯。

MultiInfo内部字段就是上面 SQL 的查询结果集合,它不承担业务行为,纯粹当作数据传输对象。在 JSP 页面用 EL 表达式直接取字段,渲染表格:

<c:forEach items="${applicantList}" var="item"> <tr> <td>${item.name}</td> <td>${item.education}</td> <td>${item.roundResult}</td> <td>${item.score}</td> </tr> </c:forEach>

如果 JSP 页面直接写 Java 脚本片段,代码里一定会出现<%标签,那会让页面难以维护。想让项目更有工程味道,就尽量引导使用 JSTL 和 EL 表达式。

5. 部署与代码评审避坑:六条常见的课设翻车记录

5.1 Tomcat 启动后访问全是 404

  • 现象:Tomcat 能正常启动,日志也没报错,但访问http://localhost:8080/resume/login.jsp时页面 404。
  • 原因:Artifact 没有正确部署到 Tomcat,或 Application context 设置成了/,实际访问路径与部署路径不一致。
  • 解决:检查 IDEA 的 Run Configuration 中 Deployment 标签页,确认 Artifact 已加入且 Application context 是/resume。启动后在 Tomcat 的webapps目录看是否自动生成了resume文件夹,如果没有,说明部署失败,删除 Tomcat 配置里的部署项后重新添加。

5.2 数据库连接报Public Key Retrieval is not allowed

  • 现象:首次访问页面时抛出 SQLNonTransientConnectionException,提示Public Key Retrieval is not allowed。
  • 原因:MySQL 8.0 默认使用 caching_sha2_password 认证,客户端连接时默认不允许获取公钥。
  • 解决:在 JDBC URL 后面追加allowPublicKeyRetrieval=true&useSSL=false。完整连接串是这样:
jdbc:mysql://localhost:3306/resume_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai

5.3 JSP 页面中文全部乱码

  • 现象:页面输出中文变成问号或乱码,数据库里存进去的数据也是乱码。
  • 原因:数据流环节中有至少一处字符编码不是 UTF-8。
  • 解决:按三层排查。第一,数据库连接串加characterEncoding=utf8;第二,JSP 页面顶部写<%@ page contentType="text/html;charset=UTF-8" language="java" %>;第三,ServletdoPost开头加request.setCharacterEncoding("UTF-8")。如果用了 Tomcat 8.5 以上,GET 请求默认 UTF-8,不需要额外配置 URIEncoding。

5.4ClassNotFoundException: com.mysql.jdbc.Driver

  • 现象:Tomcat 启动后调用数据库操作时报类找不到驱动。
  • 原因:MySQL 驱动 jar 没有放进 Artifact 的 WEB-INF/lib 目录,或者驱动版本与 MySQL 版本不匹配。
  • 解决:在 IDEA 的 Project Structure -> Artifacts -> Output Layout 中右键加入 Library Files,把 mysql-connector 的 jar 加进去,重新 Build Artifact 后重启 Tomcat。直接放在项目/WEB-INF/lib目录下是最稳妥的做法,不要放在 JDK 的扩展目录里。

5.5 简历创建报外键约束失败

  • 现象:创建简历时提示Cannot add or update a child row: a foreign key constraint fails。
  • 原因:代码里插入教育经历或工作经历时,传给外键字段的 applicant_id 是 0 或 null。典型的错误是 Dao 里INSERT ... RETURN_GENERATED_KEYS没有执行,或没有调ResultSet的next()。
  • 解决:在insertResume方法执行后打印主键值确认。如果主键获取成功,再去教育经历表的 insert 方法里逐行打个断点核对参数顺序,很多情况下是ps.setInt(index, value)的 index 插错位。

5.6 Session 失效后操作报 NullPointerException

  • 现象:长期停留在页面不操作,再点某个链接时后台空指针异常,堆栈定位到session.getAttribute("currentUser").getId()这一行。
  • 原因:Session 默认 30 分钟过期,currentUser变成 null。
  • 解决:在 Servlet 入口处统一加判断,或写一个过滤器校验登录态。核心代码可以这样做:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; HttpSession session = req.getSession(false); String uri = req.getRequestURI(); if (uri.contains("login") || uri.endsWith(".css") || uri.endsWith(".js")) { chain.doFilter(request, response); return; } if (session == null || session.getAttribute("currentUser") == null) { resp.sendRedirect("login.jsp"); return; } chain.doFilter(request, response); }

在web.xml里注册这个过滤器,url-pattern设为/*。这一处改动直接决定答辩时老师对系统完整度的直观印象。

6. 给课设加加分:把简历数据做成可视化统计与 Excel 导出

系统本身是管理型应用,课设答辩时如果能拿出数据可视化的页面,会让人觉得这个项目“超出预期”。我个人通常会在原系统里增加两个功能:一个按学历分布统计柱状图,另一个把面试结果导出为 Excel 文件。

6.1 用 ECharts 展示学历分布

不引入后端图表库,直接让 Dao 层提供一个统计方法,返回每个学历对应的人数,前端用 ECharts 画图。Dao 层新增方法:

public List<Map<String, Object>> countByEducation() { List<Map<String, Object>> result = new ArrayList<>(); String sql = "SELECT education AS name, COUNT(*) AS value FROM applicant GROUP BY education"; // 省略 JDBC 执行细节 return result; }

对应新增一个统计 Servlet,把返回的 JSON 写入 response:

response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); out.write(new Gson().toJson(countList)); out.flush();

前端页面引入 ECharts:

$.ajax({ url: 'statisticsServlet', dataType: 'json', success: function(data) { var chart = echarts.init(document.getElementById('main')); chart.setOption({ tooltip: {}, xAxis: { data: data.map(item => item.name) }, yAxis: {}, series: [{ type: 'bar', data: data.map(item => item.value) }] }); } });

重点是在答辩时说明统计 SQL 的聚合逻辑:GROUP BY education把相同学历的记录合并,COUNT(*)统计每一组行数,这种方式比在 Java 内存里循环统计要高效得多。

6.2 导出 Excel:用 POI 生成面试结果

导出功能在课设项目里出现频率不高,如果实现了,含金量会明显提升。核心代码如下:

public void exportInterviewResult(List<MultiInfo> list, HttpServletResponse response) throws IOException { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("面试记录"); String[] headers = {"姓名", "学历", "面试官", "结果", "备注"}; Row headerRow = sheet.createRow(0); for (int i = 0; i < headers.length; i++) { headerRow.createCell(i).setCellValue(headers[i]); } int rowNum = 1; for (MultiInfo info : list) { Row row = sheet.createRow(rowNum++); row.createCell(0).setCellValue(info.getName()); row.createCell(1).setCellValue(info.getEducation()); row.createCell(2).setCellValue(info.getInterviewer()); row.createCell(3).setCellValue(info.getRoundResult()); row.createCell(4).setCellValue(info.getRemark()); } for (int i = 0; i < headers.length; i++) { sheet.autoSizeColumn(i); } response.setContentType("application/vnd.ms-excel;charset=utf-8"); response.setHeader("Content-Disposition", "attachment;filename=interview_result.xlsx"); workbook.write(response.getOutputStream()); workbook.close(); }

XSSFWorkbook是 POI 操作 Excel 2007 及以上格式的入口,autoSizeColumn会根据内容自动拉宽列。注意响应头里Content-Disposition的filename用 ASCII 命名,避免不同浏览器中文文件名乱码。

从那以后我每次拿到一个课设源码,都会按同样的顺序做三件事:打开 Dao 层看 SQL 是否全部用 PreparedStatement,打开 Servlet 看 Session 判断是否覆盖所有入口,最后看数据库脚本有没有设置外键。三个检查做完,这个项目能不能跑、能不能答辩、值不值得看,心里大概就有数了。本次拆解的资源在这三件事上都没有敷衍,只要把环境对齐就能顺利启动。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 15:04:59

基于Vue和Spring Boot的在线考试系统全栈开发实践

1. 项目概述在线考试系统是教育信息化进程中的重要组成部分&#xff0c;它彻底改变了传统纸质考试的运作模式。作为一名参与过多个在线考试系统开发的技术人员&#xff0c;我想分享一个基于Web的完整解决方案&#xff0c;包含前端界面、后端逻辑和数据库设计的全栈实现。这个系…

作者头像 李华
网站建设 2026/10/14 15:04:58

WX‑0813/ AI‑ENC 降噪/ 100dB 回声消除 / 双 5W 功放/USB 免驱一站式语音

标签&#xff1a;# 语音模组 #AI 降噪 #AEC 回声消除 #DSP 音频 #嵌入式音频 #全双工对讲前言做对讲、会议、门禁、监控拾音类硬件&#xff0c;经常遇到几个老大难问题&#xff1a;环境噪声大、喇叭麦克风距离近回声严重、还要额外设计功放电路&#xff0c;主控平台多还需要适配…

作者头像 李华
网站建设 2026/10/14 15:04:54

背单词总忘怎么办黑龙江家长可试这三步

哈尔滨一位四年级家长跟我聊&#xff0c;孩子每晚抄单词抄到十点半&#xff0c;第二天听写还是错一半。她翻着本子给我看&#xff0c;每个词抄了二十遍&#xff0c;字迹工整&#xff0c;可合上本子就忘。这不是态度问题&#xff0c;是方法把“手在动、脑没动”当成了记忆。词汇…

作者头像 李华
网站建设 2026/10/14 15:04:38

jujutsu 版本控制工具

简介 jujutsu 是一款与 git 互补的工具 典型用法 TODO: here

作者头像 李华
网站建设 2026/10/14 15:03:46

存储墙与功耗墙之后:为什么根底座才是未来计算的地基

一次深夜的性能剖析&#xff0c;让我真正理解了那句看起来有点耸人听闻的判断——冯诺依曼已死&#xff0c;根底座才是未来。当时我在做一个数据召回任务&#xff0c;CPU占用率还不到一半&#xff0c;内存带宽却已经打满。数据从内存搬到寄存器&#xff0c;再从寄存器搬回去&am…

作者头像 李华