简介基于Java和MySQL实现的网上作业批改系统面向Java初学者及课程设计/毕业设计开发者提供了从后端逻辑到前端页面的完整可运行方案。资源包共265个文件压缩后仅1.93MB包含45个JSP页面、24个Java源文件及对应Class文件另有CSS/JS负责界面样式与交互SQL脚本用于快速建库DOC/DOCX文档辅助理解设计思路。系统以管理员为核心实现了班级信息管理、教师与学生信息管理、公告与留言管理、教学资料管理等功能模块注重操作简便与安全防护。已有1615人学习下载适合需要快速搭建在线作业批改场景、参考典型权限管理或多模块Web项目结构的开发者使用。1. 网上作业批改系统JAVAMysql是什么先看清这套系统到底解决谁的问题拿到“网上作业批改系统JAVAMysql.zip”这个压缩包的人通常是三种身份要交课程设计的在校生、刚入职想找一个完整 Java Web 项目练手的初级开发以及需要快速给客户搭演示环境的实施人员。这个标题看起来像是一份课程设计源码但说透了它代表一条完整的落地链路学生在线提交作业教师在线批改打分管理员统一管理课程与账号数据全部落进 MySQL。我对这类系统的判断是业务逻辑本身并不复杂最容易让新手翻车的反而是环境与数据两项。JDK、Tomcat、MySQL 三个组件的版本怎么配SQL 脚本怎么导入连接串里的参数少写一个会报什么错这些坑比写代码更消耗时间。所以这篇文章按“先看业务设计、再跑通本地、最后排查问题”的顺序把一条可复现的路径完整拆开。读完你不仅能把系统跑起来还能知道批改数据是怎么组织的、哪些参数动了会出问题、上线前还要补什么功课。2. 从zip到能登录把网上作业批改系统跑起来的钥匙与最小命令拿到压缩包先别急着解压先想清楚一件事这套系统最常见的骨架是 JSP Servlet JDBC更工程化的版本会换成 Spring MVC MyBatis但不管哪种运行前提都是三样东西——JDK、Tomcat、MySQL。许多课程设计系统中JDK 8 与 Tomcat 8.5 的组合最稳MySQL 5.7 或 8.0 都能用但驱动类名不同这一步错了启动必翻车。2.1 开跑前先核对环境版本JDK、Tomcat、MySQL的匹配关系先把环境匹配关系钉死后面少走弯路。我一般按下面这张表来配组件推荐版本说明JDK1.8 或 111.8 最兼容老项目11 需要 Tomcat 9Tomcat8.5 或 9.0与 JDK 版本对应8.5 配 JDK89.0 配 JDK11MySQL5.7 或 8.08.0 默认加密插件变化大驱动类名也变了JDBC 驱动5.1.x / 8.0.xMySQL5.7 用 com.mysql.jdbc.Driver8.0 用 com.mysql.cj.jdbc.DriverMySQL 8.0 的加密规则默认是 caching_sha2_password老驱动连接会报认证失败MySQL 5.7 用默认密码规则即可。如果你在 Windows 上装 MySQL 8常见做法是下载 ZIP 解压后初始化Windows 上 mysql 安装配置教程一般会提到 my.ini 里加default_authentication_pluginmysql_native_password这一行目的就是兼容老项目。Linux 上则直接apt install mysql-server或yum install mysql-community-server装完先systemctl start mysqld。版本核对命令也很简单三条就能看全java -version mysql --version /opt/tomcat/bin/version.sh | grep Server version三条命令分别确认 JDK 编译版本、MySQL 版本和 Tomcat 主版本。版本对不上时优先降 Tomcat 而不是升 JDK——老项目的web.xml头声明如果是 Servlet 2.5放到 Tomcat 10 上会因为javax.servlet包路径变更而直接 404。2.2 建库、导数据、改连接串项目包里的SQL脚本怎么用项目包里通常会有一个sql目录放着建库脚本或完整备份。常见的命名是homework.sql或db_init.sql。不要双击打开复制粘贴到 Navicat 里执行大文件容易卡死命令行导入更稳。先建库再导入mysql -h 127.0.0.1 -u root -p \ -e CREATE DATABASE IF NOT EXISTS homework_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; mysql -h 127.0.0.1 -u root -p homework_db /path/to/homework.sql第一条命令把库建出来字符集直接指定 utf8mb4把以后做中文作业内容、表情符号扩展名的文件都兼容掉。第二条命令把表结构和初始数据导入到 homework_db。-h 127.0.0.1是强制走 TCP 连接不是走本地 socket 文件这一步能提前绕开一个高频报错后面避坑章节会专门讲。导入之后去改项目的数据库配置。常见做法是项目里有一份jdbc.properties或db.properties内容大概是这个样子jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://127.0.0.1:3306/homework_db?useSSLfalseuseUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456useSSLfalse是给开发环境省事MySQL 8 默认开 SSL不关会报 SSL 握手异常characterEncodingutf8与库里指定的 utf8mb4 对齐页面传中文不乱码serverTimezone必加否则 JDBC 驱动拿不到 JVM 默认时区会直接抛异常allowPublicKeyRetrievaltrue是配合 MySQL 8 的 caching_sha2_password 使用第一次连接需要取公钥不写也会报错。这四个参数是这套系统能跑起来的“后悔药”组合缺一个就多一次踩坑机会。2.3 启动 Tomcat用前台模式盯日志别用启动脚本一闪而过环境配好后启动 Tomcat 也有讲究。很多人直接双击startup.bat或执行startup.sh然后看到窗口一闪而过以为成功了其实端口没起或者项目没部署。正确做法是前台运行日志直接打到终端cd /opt/tomcat/bin ./catalina.sh run前台模式的好处是启动报错当场可见比如端口占用、类加载异常、数据库连接超时都会直接刷在终端上。看到Server startup in [xxx] milliseconds后再停掉改回startup.sh后台启动也不迟。浏览器访问http://localhost:8080/项目名/项目名要看webapps目录下解压出来的文件夹名比如webapps/homework就是http://localhost:8080/homework/。如果你改过server.xml里的端口则对应换成新端口。到这里能用账号密码登录进去说明这套 JAVAMysql 组合的网上作业批改系统已经在本地站住了。3. MySQL侧的核心设计看清作业与批改数据怎么组织再动手改把系统跑起来只是第一步。真正要改功能、加字段、调统计你必须看懂数据库侧的设计。网上作业批改系统说白了就是三类数据用户、作业、批改关系。作业提交后产生一条状态记录教师批改后更新这条记录的分数和评语管理员的统计报表再从这些表里汇总。数据库设计不清楚改一个功能就会牵连三张表。3.1 用户、作业、批改三张主表字段类型与索引怎么定先看核心表结构。用户表区分学生、教师、管理员三种角色作业表描述“某门课程在某个截止时间前要交什么”批改表记录“某位学生的某份作业得了多少分”。下面是一个常见的建表脚本CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, account VARCHAR(32) NOT NULL UNIQUE, role TINYINT NOT NULL COMMENT 1学生 2教师 3管理员, password_hash VARCHAR(64) NOT NULL, real_name VARCHAR(32) DEFAULT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_homework ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_id BIGINT NOT NULL, title VARCHAR(128) NOT NULL, deadline DATETIME NOT NULL, creator_id BIGINT NOT NULL, INDEX idx_course_id (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_review ( id BIGINT PRIMARY KEY AUTO_INCREMENT, homework_id BIGINT NOT NULL, student_id BIGINT NOT NULL, submit_time DATETIME DEFAULT NULL, file_path VARCHAR(255) DEFAULT , score DECIMAL(5,2) DEFAULT NULL, comment TEXT, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待批改 1已批改 2已退回, reviewed_by BIGINT DEFAULT NULL, reviewed_at DATETIME DEFAULT NULL, UNIQUE KEY uk_homework_student (homework_id, student_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;t_review表是整个系统的关键。首先唯一键uk_homework_student保证同一个学生对同一份作业只能有一条记录这是防止重复提交的数据库级护栏。其次status字段承担状态流转0、1、2 三个值就能表达“待批改、已批改、已退回”的完整生命周期。score DECIMAL(5,2)能存到三位整数加两位小数课程设计场景足够如果以后要存百分制加权分也不用加精度。索引方面t_homework的idx_course_id是为“按课程列表展示作业”服务的t_review上homework_id与student_id的组合唯一索引同时充当了查询索引。这里不要盲目加索引作业批改系统的数据量级在万级以下索引过多反而拖慢写入。MySQL 排序和联结在这种量级上没有任何压力不需要分区也不需要引入中间表。3.2 用存储过程统计提交率与平均分把复杂查询收进数据库业务上有个高频需求教师想看“这门课有多少人交了、平均分多少”。如果都在 Java 代码里写循环查询一次统计要发几十条 SQL数据库连接池都被拖慢。常见做法是把统计逻辑写进存储过程应用层只调一个CALL语句。DELIMITER $$ CREATE PROCEDURE sp_course_stats(IN p_course_id INT) BEGIN SELECT c.course_name, COUNT(DISTINCT u.id) AS total_students, COUNT(DISTINCT CASE WHEN r.status 1 THEN r.student_id END) AS submitted, ROUND(AVG(CASE WHEN r.status 1 THEN r.score END), 2) AS avg_score FROM t_course c JOIN t_user u ON u.role 1 LEFT JOIN t_homework h ON h.course_id c.id LEFT JOIN t_review r ON r.homework_id h.id AND r.student_id u.id WHERE c.id p_course_id GROUP BY c.course_name; END$$ DELIMITER ;两个细节要注意。第一DELIMITER是命令行客户端的语法Navicat 里执行时可以去掉DELIMITER那两行。第二调用时直接CALL sp_course_stats(1)参数是课程 ID。这里的LEFT JOIN是关键——如果某学生没交作业t_review里没有对应记录内联结会把他丢掉统计就偏了。存储过程不是越多越好把“一条 SQL 能查清的多表关联”写成过程是过度设计但像这种提交率统计涉及三张表关联且会被多个页面重复调用放进数据库反而让 Java 代码更干净。3.3 数据库连接池参数这五个值决定系统能扛多少并发系统跑起来后最容易出现的隐患是连接池配置不合理。很多新手项目直接用 JDBC 直连每次请求都DriverManager.getConnection()数据库连接创建非常昂贵几十个人同时交作业就会卡死。所以要引入连接池Druid 是 Java Web 项目里最常见的选择。bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://127.0.0.1:3306/homework_db?useSSLfalsecharacterEncodingutf8/ property nameusername valueroot/ property namepassword value123456/ property nameinitialSize value5/ property namemaxActive value20/ property nameminIdle value5/ property namemaxWait value60000/ property namevalidationQuery valueSELECT 1/ /bean参数没调好系统在线人数 50 以内可能完全正常一上实训课 100 人同时交作业就崩。我一般建议课程设计系统用initialSize5够支撑启动时的基础连接maxActive20对应一个班 40 人同时操作绰绰有余minIdle5保持最低空闲连接避免请求突发时要现建连接maxWait60000毫秒请求拿不到连接最多等 60 秒就报超时而不是无限阻塞把线程池拖死validationQuerySELECT 1是让连接池定期检测连接是否存活MySQL 8 的 wait_timeout 默认 8 小时空闲连接容易被服务端回收没有心跳检测就会拿到失效连接。这套 MySQL 连接池配置直接决定了系统的稳定上限。项目里如果是 c3p0 或 HikariCP参数名不同但思路一致——核心就是“最小连接数、最大连接数、等待超时、探活语句”这四个维度。4. 批改业务的核心链路上传、判分与状态流转改在哪系统跑通、数据库看清之后大多数人的真实需求是改功能限制文件类型、加一个退回重交、按班级导出成绩。这些都落在批改业务链路里。这条链路包括三块学生上传、教师批改、状态更新。4.1 学生端上传作业文件存磁盘数据库只存路径作业上传是批改系统的入口也是新手最容易写错的地方。常见错误是把文件转成 Base64 字符串存进 MySQL 的 TEXT 字段几 MB 的文档就会让表膨胀、查询变慢。正确做法是文件落磁盘数据库只记录文件的相对路径。WebServlet(/upload) public class UploadServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); Part filePart req.getPart(file); long studentId Long.parseLong(req.getParameter(studentId)); long homeworkId Long.parseLong(req.getParameter(homeworkId)); // 文件命名规则学号_作业ID_时间戳.扩展名防止重名覆盖 String ext getExtension(filePart.getSubmittedFileName()); String fileName studentId _ homeworkId _ System.currentTimeMillis() . ext; // 常见做法存到 webapps 外的独立目录避免重启丢文件 String basePath /data/homework_files; File dir new File(basePath); if (!dir.exists()) { dir.mkdirs(); } filePart.write(basePath File.separator fileName); // 更新 t_review 表的 file_path 和 status状态改为 0待批改 ReviewDao dao new ReviewDao(); dao.markSubmitted(studentId, homeworkId, fileName); } }这段代码有两个关键点。第一文件名用“学号 作业 ID 时间戳”拼出来既保证唯一又方便排查“谁交的哪份作业”只用原始文件名的话两个班各有一个“张三”、文件都叫“作业1.docx”必覆盖。第二文件写到 webapps 目录之外这样 Tomcat 重启或重新部署 war 包时文件不会丢后面迁移存储也只是改一个基础路径的事。数据库里的file_path存相对路径前端展示时再拼完整下载地址。filePart.write()是 Servlet 3.0 的 Part 接口写文件的方法比较适合课程设计代码量真实生产再换成FileOutputStream分批写区别在于大文件上传内存占用更可控。4.2 教师端批改与评分一条 UPDATE 完成状态流转教师端批改页面做的事很简单显示待批改列表点开一份作业填写分数和评语提交。后端对应就是更新t_review表的一条记录。public boolean reviewHomework(long reviewId, BigDecimal score, String comment, long teacherId) { String sql UPDATE t_review SET score ?, comment ?, status 1, reviewed_by ?, reviewed_at NOW() WHERE id ? AND status 0; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setBigDecimal(1, score); ps.setString(2, comment); ps.setLong(3, teacherId); ps.setLong(4, reviewId); return ps.executeUpdate() 1; } catch (SQLException e) { throw new RuntimeException(批改失败请检查连接池状态, e); } }注意 SQL 里的AND status 0条件——这是在数据库层面保证只有“待批改”状态的记录才能被批改。两个老师同时打开同一份作业后提交的人会因为条件不匹配而更新 0 行避免互相覆盖。这个设计比在 Java 代码里加 synchronized 更可靠因为并发控制放到了数据库的原子层面这正是 java 面试题里常问的“数据一致性”的朴素实现。PreparedStatement这里必须用不能用字符串拼接 SQL。作业批语是自由文本学生完全可以在评语里写一段 OR 11之类的注入语句拼接 SQL 直接就把查询条件改了。用setBigDecimal和setString绑定参数从根上断绝注入可能同时 BigDecimal 还避免了浮点运算的精度误差。4.3 成绩排名与不及格预警用 MySQL 排序把统计页撑起来批改完成后教师端通常要按成绩排名看整体情况。这条 SQL 覆盖了“按分数倒序、同分按学号升序”的二级排序场景SELECT s.real_name AS student_name, r.score, r.comment FROM t_review r JOIN t_user s ON s.id r.student_id WHERE r.homework_id 5 AND r.status 1 ORDER BY r.score DESC, s.account ASC LIMIT 50;ORDER BY r.score DESC做分数降序s.account ASC作为次级排序让同分学生按学号排列输出结果稳定可预期。LIMIT 50在这里是保护性写法先只取前 50 名给页面第一屏避免一次渲染几千行。统计页再配合第 3 章的存储过程提交率、平均分、最高分这些指标就能一站查全。这里有一个容易忽视的边界status 1的条件不能省。如果不加状态为 2已退回的作业也会带着一个旧分数参与排名未批改的记录score为 NULL在排序中会被排在最后造成排名数据污染。写统计 SQL 时把“只统计已批改”当作默认前提就能避免这个逻辑黑洞。5. 跑这套JAVAMysql系统常见的5个坑从启动到上线的排查清单环境类问题占这类系统排障的八成以上。以下五条是我接手网上作业批改系统时最常碰见的坑按出现频率排序每条都按“现象 → 原因 → 解决”写可以直接照着排查。5.1 mysql ssl连接错误启动时握手失败的惯犯现象Tomcat 启动日志报Communications link failureCaused by 是SSLHandshakeException或提示Establishing SSL connection without servers identity verification is not recommended。原因MySQL 8.0 默认开启 SSL 连接客户端尝试加密握手但证书验证不过MySQL 5.7 在某些发行版上也会出现类似告警。项目里的连接串没有显式声明是否使用 SSL驱动就按默认行为加密。解决开发环境在 JDBC 连接串上加useSSLfalse。如果 MySQL 8.0 还报Public Key Retrieval is not allowed再加allowPublicKeyRetrievaltrue。完整连接串在第 2.2 节已经给出直接抄用。生产环境不建议关 SSL但生产排错时可以先临时加这两个参数定位是不是 SSL 引发的确认后再配置证书。5.2 ERROR 2002 (HY000)cant connect to local MySQL server through socket /tmp/mysql.sock现象命令行执行mysql -u root -p直接报ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock (2)。原因客户端默认走 socket 文件连接但服务端 socket 路径不在/tmp/mysql.sock。常见于 MySQL 5.7 通过源码或 RPM 安装socket 被配置到/var/lib/mysql/mysql.sock或/var/run/mysqld/mysqld.sock另一种可能是 mysqld 压根没启动。解决先确认服务是否在跑systemctl status mysqld # 或 service mysql status没跑就启动跑了还报错就改用 TCP 方式连接避开 socket 路径问题mysql -h 127.0.0.1 -P 3306 -u root -p-h 127.0.0.1强制走 TCP这是最快绕过 socket 路径差异的命令。根源修复则要在/etc/my.cnf里找到socket配置把客户端[client]段的 socket 路径指到与[mysqld]段一致。正因为这个坑高频我在第 2.2 节的建库命令里刻意用了-h 127.0.0.1就是提前避开它。5.3 中文乱码连接串、表字符集、页面编码三处不一致现象作业标题明明是中文页面上显示成问号或者数据库里查出来正常页面渲染乱码。原因乱码从来不是一处问题而是三处没对齐。连接串里的characterEncoding、MySQL 表默认字符集、JSP 页面的pageEncoding各自为政任何一处是 latin1 或 GBK 都会出乱码。解决三层统一为 UTF-8。连接串加useUnicodetruecharacterEncodingutf8建表时指定DEFAULT CHARSETutf8mb4JSP 页面头部写% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %。另外如果用了过滤器request.setCharacterEncoding(UTF-8)要放在读取任何请求参数之前。检查顺序是先看数据库里存的值对不对再看页面请求进来了没有最后看响应输出编码。数据库里已是乱码的话改完代码后要重新导入数据不要指望已有脏数据自动恢复。5.4 页面偶发超时连接池耗尽而不是程序死循环现象系统运行一段时间后某个页面偶尔卡住等 30 秒以上才报错Tomcat 日志出现Connection is not available, request timed out after 30000ms。原因获取的数据库连接没归还。最常见是代码里只conn.createStatement()不关conn或者查询抛异常后没有走 finally 释放连接。连接池maxActive20每次请求漏掉一条连接20 个请求后池子就空了。解决先用第 3.3 节的连接池配置把maxWait设为可见超时让问题暴露而不是无限等待。然后用这段固定写法检查自己的 DAOtry (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { // 业务处理 } catch (SQLException e) { throw new RuntimeException(查询失败, e); }Java 7 的 try-with-resources 会自动关闭Connection、PreparedStatement、ResultSet三个资源且关闭顺序与创建顺序相反。这是我在所有课程设计代码评审里必查的点——只要看到手动finally里只关了conn没关rs和ps基本就是连接池耗尽的定时炸弹。5.5 端口被占用地址已在使用中现象Tomcat 启动即失败日志报Address already in use: JVM_BindMySQL 启动失败报Bind on TCP/IP port: 3306。原因本机已有另一个进程占用了 8080 或 3306。常见于开发机装过多个版本的 Tomcat、MySQL 服务自启动或 IDE 内嵌的 Tomcat 没停干净。解决先找到占用进程再决定是杀进程还是改端口。# Linux / macOS lsof -i:8080 # Windows netstat -ano | findstr 8080找到 PID 后按需处理。不改杀进程的话Tomcat 的端口在conf/server.xml里统一改三个地方Connector port8080的 HTTP 端口以及两个port8005、port8009的关闭与 AJP 端口。许多人只改了 8080 忘改 8005照样启动失败。改端口后访问地址同步变化这个坑在演示环境翻车率极高。6. 上线前最该做的三件事验证数据、预编译防注入、把war部署到Linux系统能在本机跑通只能算“能演示”要真正投入使用还差一轮验证和加固。我接这类系统时上线前必做三件事造数据验证统计、检查 SQL 注入、把部署环境从 Windows 迁到 Linux。第一用 Navicat 批量造数据验证成绩排名和存储过程是否按预期工作。在t_review表里插入 60 个学生的成绩记录故意构造同分、缺考、退回三种边界数据然后执行第 4.3 节的排名 SQL看同分学生的排序是否按学号稳定输出。这一步能发现 SQL 条件里的逻辑漏洞比上线后等真实用户踩雷成本低得多。第二全项目检索字符串拼接 SQL 的地方。最直接的办法是搜 出现在 SQL 字符串里的代码// 错误写法字符串拼接用户输入 String sql SELECT * FROM t_user WHERE account account ; // 正确写法预编译绑定参数 String sql SELECT * FROM t_user WHERE account ?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, account);课程设计项目往往赶工登录接口、查询接口最容易出现这种注入点。哪怕是内部系统一条 OR 11就能以第一个用户的身份登录必须全部改成PreparedStatement参数绑定。第三部署到 Linux 服务器。课程设计系统的量级不需要微服务把项目打成 war 包放到 Tomcat 的webapps目录即可。MySQL 也迁到同一台服务器或独立数据库机迁移时用mysqldump导出再导入mysqldump -h 127.0.0.1 -u root -p homework_db homework_backup.sql mysql -h 目标服务器IP -u root -p homework_db homework_backup.sql我现在的习惯是先把连接串里的useSSL、characterEncoding、serverTimezone三个参数钉死再启动服务省得后面查乱码查到怀疑人生再在连接池的maxWait上留一个可见的超时值让连接泄漏问题尽早暴露而不是卡到用户投诉。环境归环境代码归代码两者分开排查这套 JAVAMysql 的网上作业批改系统才能真正从“能跑”变成“能扛”。希望帮到你。本文还有配套的精品资源点击获取