简介一份面向计算机相关专业在校学生与毕业设计场景的完整项目源码基于MySQL与Java Swing实现学生健康档案管理系统。系统整体分解为学生信息管理、体检信息管理与病历信息管理三大模块覆盖学号、姓名、性别、系别、诊断、年龄、身高、体重、胸围、日期等核心字段并支持多选项高级查询、数据导入导出和动态折线图数据分析适合课程设计、项目初期立项演示及JavaMySQL进阶学习。压缩包共98个文件约1.27MB其中java源码30个、class编译文件38个、xml配置7个另有图片、Excel表格、SQL脚本与项目配置等可直接导入IDE运行查看效果同时包含E-R图、数据库表设计文档及工程目录便于理解项目结构。项目曾获导师指导认可答辩评审分达96分适合作为课程设计或毕业设计参考。已有76人学习下载既能帮助初学者从数据库建表、Swing界面交互到业务逻辑实现形成完整认知也可为二次开发提供可复用模板。1. 学生健康档案管理系统SwingMySQL从登录到数据导出的完整闭环如果你正缺一个能完整跑起来的课设或毕设项目“基于MySQLJava swing实现的学生健康档案管理系统 HealthArchives”值得拆开看一眼。它不炫技但把桌面信息管理系统该有的环节全串起来了建库脚本、JDBC连接、登录注册、学生/体检/病历三块数据的增删改查、Excel导入导出、多选批量操作还带一张动态折线图做数据分析。对在校学生这是一套可以照着扩展的项目骨架对已经工作的开发者用它做内部小工具也够用。下面按我从下载到跑通再到改造的顺序讲重点放在启动、配置和真正容易翻车的地方。2. 数据库设计先行从E-R图拆解到可复现的建表脚本拿到资源包后先别急着把代码往IDEA里拖。先把dbstuhealth.sql拆开看一遍因为后面Swing界面里所有增删改查的成败都藏在这份脚本里。这个系统的设计很直白病历数据和体检数据分开管理但都挂在学生信息下面所以表结构一定是先有主表、再有明细表外键关系也相对简单。下面这一步建库、导数据、核查字段只要做顺了上面跑界面就有底。2.1 三张核心表的关系主从结构一眼就能看清打开解压目录里的E-Rͼ.jpg和数据库表.jpg可以直观看到系统由三大模块组成学生信息管理、体检信息管理、病历信息管理。对应到dbstuhealth.sql里就是三张核心表。我按原文的功能描述整理出下面这张对应表业务域核心字段作用学生信息学号、姓名、性别、系别主表被体检和病历引用体检信息学号、姓名、性别、系别、年龄、身高、体重、胸围、日期记录每次体检结果病历信息学号、姓名、性别、系别、诊断、日期记录每次就诊诊断为什么体检和病历分开而不是合并成一张宽表原因是两者业务节奏完全不同体检是周期性大规模采集一次导入几十上百条病历是随时发生的个体记录频率低但内容杂。合并到一张表里要么字段冗余严重要么一张表里塞满空值。拆成两张明细表后界面上的“添加体检”和“添加病历”各管各的窗口也符合班主任或校医的实际使用习惯。表与表之间的关系也很好定位。student_info是主表physical_exam和medical_record分别以student_info的主键作为外键。参考建表脚本可以这样写-- 学生主表 CREATE TABLE IF NOT EXISTS student_info ( id INT AUTO_INCREMENT PRIMARY KEY, student_no VARCHAR(20) NOT NULL COMMENT 学号, name VARCHAR(30) NOT NULL COMMENT 姓名, gender VARCHAR(4) COMMENT 性别, department VARCHAR(40) COMMENT 系别, UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生基础档案; -- 体检明细表 CREATE TABLE IF NOT EXISTS physical_exam ( id INT AUTO_INCREMENT PRIMARY KEY, student_id INT NOT NULL, age INT, height DECIMAL(5,2), weight DECIMAL(5,2), chest DECIMAL(5,2), check_date DATE, CONSTRAINT fk_physical_student FOREIGN KEY (student_id) REFERENCES student_info(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT体检记录; -- 病历明细表 CREATE TABLE IF NOT EXISTS medical_record ( id INT AUTO_INCREMENT PRIMARY KEY, student_id INT NOT NULL, diagnosis VARCHAR(200), record_date DATE, CONSTRAINT fk_medical_student FOREIGN KEY (student_id) REFERENCES student_info(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT病历记录;逻辑说明student_no加了唯一索引避免同一个人被重复建档身高、体重、胸围用DECIMAL(5,2)能存 999.99对学生体检来说精度足够又不会像 FLOAT 那样出现浮点误差。外键约束我建议保留虽然有些课设为了插入方便会把外键删掉但实际操作中保留它至少能在你误删学生时拦住一下避免出现“病历还在、学生没了”的孤儿数据。2.2 把SQL脚本导入MySQL建库、导数据、验证表结构MySQL 的导入方式有很多我习惯在命令行里直接执行。这个资源包里既然给了.sql文件最稳妥的路径是先手工建库再导入mysql -u root -p进入 MySQL 命令行后执行CREATE DATABASE IF NOT EXISTS healtharchives DEFAULT CHARACTER SET utf8mb4; USE healtharchives; SOURCE /your/path/dbstuhealth.sql;如果你是 Windows 环境路径里的反斜杠会有转义问题建议在 MySQL 命令行里用正斜杠写路径。也可以跳过交互直接一行导入mysql -u root -p healtharchives dbstuhealth.sql导入完成后第一时间SHOW TABLES;看输出。如果能看到三张核心表说明导入基本成功。这里有一个高频翻车点脚本内部如果写了USE 某个库名而你手动建的库名和它不一样导入后表会跑到脚本指定的库里你在当前库自然是空的。所以导入前先用文本编辑器打开.sql文件搜一下CREATE DATABASE和USE确认库名一致。接着逐表核对字段。比如DESC student_info;输出的重点是三处第一student_no是否唯一第二日期字段是不是DATE而不是DATETIME第三字符串字段有没有给够长度。原始 Excel 里如果存在超过 20 位的学号VARCHAR(20)会在导入时报数据超长后面所有查询都会连带报错。2.3 导入数据前的三条预处理日期格式、编码和索引资源包里有三份 Exceldata.xls、data1.xls、data2.xls。这三份大概率是演示数据或导入模板字段顺序基本决定 Swing 界面真正读取时的映射方式。我一般会先把其中一份用文本编辑器打开确认编码。常见的正确姿势是先把 Excel 另存为 CSV再用LOAD DATA批量灌进 MySQLLOAD DATA LOCAL INFILE /your/path/students.csv INTO TABLE student_info CHARACTER SET utf8mb4 FIELDS TERMINATED BY , IGNORE 1 LINES (student_no, name, gender, department);逻辑说明IGNORE 1 LINES跳过表头行。如果你的 CSV 是从 Excel 另存的Excel 在中文 Windows 下默认存成 GBK这样LOAD DATA进来后大概率乱码。我一般会先用 VS Code 或 Notepad 把 CSV 转成 UTF-8 再导省得后面排查字符集。日期列同理Excel 里如果是2024/5/1这种格式导入前统一成2024-05-01否则 MySQL 的DATE字段不一定认。第三条预处理是给体检表加一个复合索引专门服务后面动态折线图的按日期查询ALTER TABLE physical_exam ADD INDEX idx_student_date (student_id, check_date);这个索引对几万条以内的课设数据体感不明显但如果你后续把它扩成全校档案按学生加时间区间的查询会平滑很多。索引不是越多越好像这种固定组合查询一对索引就够。3. JDBC链路搭建驱动依赖、连接参数与登录窗口打通数据库准备好了接下来是 Java 端。这个资源是 Maven 工程目录里有pom.xml用 IDEA 直接 Open 就能识别。很多人卡在“代码没问题但跑不起来”九成是依赖没进来或者 JDBC URL 参数不兼容本地 MySQL 版本。这一章就按依赖、连接类、登录三块讲每块都有参数能抄。3.1 项目依赖怎么摆pom.xml 与 IDEA 的导入方式打开pom.xml最核心的依赖是 MySQL 驱动。你本机装的 MySQL 版本决定了用哪个驱动坐标。如果是 MySQL 8.0dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.32/version /dependency如果是 MySQL 5.7用 5.1.49 这个版本更省事dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency逻辑说明MySQL 8 的驱动类名是com.mysql.cj.jdbc.Driver5.x 是com.mysql.jdbc.Driver。这个差异会在第 5 章专门展开。IDEA 打开项目后如果右侧 Maven 面板一直在转或者左下角提示依赖未解析先点一下刷新按钮。如果公司内网拉不了 Maven 中央仓库直接去lib目录下找有没有现成的驱动 jar手动 Add as Library 也能跑。资源目录里的jarRepositories.xml、compiler.xml是 IDEA 自己的配置不影响运行。还有一点要注意如果这个项目之前是别人在 Windows 上开的target目录里可能留着旧的编译产物导入后最好先mvn clean一次避免旧 class 文件和新代码错位。3.2 DBUtil连接类URL上的每个参数都是经验值连接类我一般单独放一个DBUtil避免每个窗口都写一遍DriverManager.getConnection。参考实现package com.health.util; import java.sql.*; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/healtharchives ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/Shanghai useSSLfalse allowPublicKeyRetrievaltrue; private static final String USER root; private static final String PASSWORD root; public static Connection getConnection() throws SQLException { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new SQLException(找不到MySQL驱动请检查依赖, e); } return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (Exception ignored) {} } if (stmt ! null) { try { stmt.close(); } catch (Exception ignored) {} } if (conn ! null) { try { conn.close(); } catch (Exception ignored) {} } } }参数说明useUnicodetruecharacterEncodingutf8保证中文进出不出乱码serverTimezoneAsia/Shanghai是 MySQL 8 驱动强制要求不写会直接报时区错误useSSLfalse关掉本地开发的 SSL 证书校验allowPublicKeyRetrievaltrue解决 MySQL 8 默认认证插件第一次握手拿不到公钥的问题这个可以算必加项。把账号密码写死在类里只适合本地调试。我一般会抽一个db.properties放到src/main/resources下jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/healtharchives?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue jdbc.userroot jdbc.passwordroot代码里用Properties加载以后换库换密码不用改 Java 源码。答辩时老师问“数据库密码泄露怎么办”也能答上来。close方法里三个try {} catch (Exception ignored) {}是 JDBC 的常见处理方式关闭资源如果抛异常说明底层连接已经不可用没必要再往上抛影响主流程。3.3 登录与注册从JFrame到业务校验的完整链路登录窗口在这个资源包里对应登录注册.jpg。用户输入账号密码点击登录后调到主界面。核心逻辑如下public class LoginFrame extends JFrame { private JTextField tfUser; private JPasswordField pfPwd; public LoginFrame() { // 初始化界面组件登录按钮绑定事件 btnLogin.addActionListener(e - login()); } private void login() { String username tfUser.getText().trim(); String password new String(pfPwd.getPassword()).trim(); if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, 用户名和密码不能为空); return; } try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement( SELECT id FROM sys_user WHERE username? AND password?)) { ps.setString(1, username); ps.setString(2, password); ResultSet rs ps.executeQuery(); if (rs.next()) { new MainFrame().setVisible(true); dispose(); } else { JOptionPane.showMessageDialog(this, 账号或密码错误); } } catch (SQLException ex) { JOptionPane.showMessageDialog(this, 数据库连接失败 ex.getMessage()); } } }这里有两个细节比登录本身更重要。第一用PreparedStatement而不是Statement拼接 SQL否则用户输入 or 11 --这种字符串可能绕过密码校验这就是典型的 SQL 注入。第二dispose()要调用否则登录窗口关不掉主界面和登录界面叠在一起看起来像卡死。另外如果sys_user表里密码是明文存储建议要么在注册界面加一层 MD5 或 SHA-256要么直接把存量数据重置不然被问到安全设计时会很被动。4. 核心功能落地体检/病历的增删改查与Excel导入导出登录进去后系统的核心就集中在三块学生信息维护、体检记录维护、病历记录维护。这章按业务动作拆成三个小节每一段代码都直接对应界面上的一个按钮。4.1 体检登记从表单到INSERT语句的字段映射体检登记窗口主要负责往physical_exam表插数据。界面上的 JTextField 拿到的都是字符串入库前要先转类型。参考方法public void addPhysical(int studentId, int age, BigDecimal height, BigDecimal weight, BigDecimal chest, Date checkDate) { String sql INSERT INTO physical_exam (student_id, age, height, weight, chest, check_date) VALUES (?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, studentId); ps.setInt(2, age); ps.setBigDecimal(3, height); ps.setBigDecimal(4, weight); ps.setBigDecimal(5, chest); ps.setDate(6, new java.sql.Date(checkDate.getTime())); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } }逻辑说明身高这类带小数的字段用BigDecimal接收避免二进制浮点误差。日期在界面上通常是java.util.Date传入 JDBC 前要转成java.sql.Date。setDate(6, ...)的 6 对应 SQL 语句里第六个问号顺序和字段列表完全一致。如果后面字段顺序调整最容易翻车的地方就是这里少了或多了问号。学生选择部分常见做法是放一个JComboBox下拉项显示“学号 姓名”实际存储student_id。比如JComboBoxStudentItem combo new JComboBox(); combo.addItem(new StudentItem(1, 20200001, 张三)); // StudentItem 的 toString() 返回 20200001 张三这样校医不用去记 ID选人也不容易选错。病历登记的逻辑完全一样只是字段换成诊断内容和日期SQL 语句换成INSERT INTO medical_record。4.2 多选修改与多选删除视图索引和事务边界资源包里的截图有“多选修改”“多选删除”这是区别于普通课设的加分点。JTable 默认单选要先打开多选模式。删除选中行的核心实现table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); public void deleteSelectedRows(JTable table) { int[] rows table.getSelectedRows(); if (rows.length 0) return; int confirm JOptionPane.showConfirmDialog( null, 确定删除选中的 rows.length 条记录吗, 提示, JOptionPane.YES_NO_OPTION); if (confirm ! JOptionPane.YES_OPTION) return; String sql DELETE FROM physical_exam WHERE id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (int i rows.length - 1; i 0; i--) { // 视图行索引转模型行索引 int modelRow table.convertRowIndexToModel(rows[i]); int id (int) table.getModel().getValueAt(modelRow, 0); ps.setInt(1, id); ps.addBatch(); } ps.executeBatch(); ((DefaultTableModel) table.getModel()).setRowCount(0); } catch (SQLException ex) { ex.printStackTrace(); } }这里两个坑很关键。第一JTable 如果做过列移动或排序视图行索引和模型行索引不是一回事直接用视图行号去取主键会拿错行。convertRowIndexToModel就是干这个的这是我见过 Swing 初学者翻车率最高的一个点。第二循环里每次addBatch攒着最后executeBatch一次性提交比每行executeUpdate快很多尤其选了几十行的时候。多选修改比删除更复杂一点因为多个选中行可能属于不同学生修改的字段通常是公共字段比如系别或者体检日期。实现思路是弹出一个对话框收集新值再循环执行 UPDATE。这里必须加事务conn.setAutoCommit(false); try { // 循环 UPDATE conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); }不加事务的话如果循环到中间一条数据主键冲突前面几条已经提交半改半不改数据就脏了。加事务是给操作上“后悔药”至少回滚到起点。4.3 Excel导入导出用POI打通数据库和表格资源包自带导入Excel.png和导出Excel.png说明系统支持从 Excel 批量读入体检数据也能把查询结果导成表格。这部分用 Apache POI 实现是主流方案。导出示例public void exportPhysicalToExcel(String savePath) throws Exception { String sql SELECT s.student_no, s.name, s.gender, s.department, p.age, p.height, p.weight, p.chest, p.check_date FROM physical_exam p JOIN student_info s ON p.student_id s.id; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery(); Workbook wb new XSSFWorkbook(); FileOutputStream fos new FileOutputStream(savePath)) { Sheet sheet wb.createSheet(体检数据); Row header sheet.createRow(0); String[] cols {学号, 姓名, 性别, 系别, 年龄, 身高, 体重, 胸围, 体检日期}; for (int i 0; i cols.length; i) { header.createCell(i).setCellValue(cols[i]); } int rowIdx 1; while (rs.next()) { Row row sheet.createRow(rowIdx); row.createCell(0).setCellValue(rs.getString(student_no)); row.createCell(1).setCellValue(rs.getString(name)); row.createCell(4).setCellValue(rs.getInt(age)); row.createCell(5).setCellValue(rs.getDouble(height)); row.createCell(6).setCellValue(rs.getDouble(weight)); row.createCell(7).setCellValue(rs.getDouble(chest)); row.createCell(8).setCellValue(rs.getDate(check_date).toString()); } for (int i 0; i cols.length; i) { sheet.autoSizeColumn(i); } } }逻辑说明导出前先做JOIN这样 Excel 里直接是学号姓名而不是 student_id 数字拿到外面的人一眼能看懂。try-with-resources把 Connection、PreparedStatement、ResultSet、Workbook、FileOutputStream 全部包进去关闭顺序由 JVM 逆序处理避免文件句柄泄漏。导入的方向相反读 Excel 第一张表逐行转成 INSERT。这里最容易踩的坑是 Excel 里给的是学号而业务表存的是 student_id。正确做法是先把学生表全量读出来放进一个HashMapString, Integer用学号当 key逐行查表转换MapString, Integer idMap new HashMap(); // SELECT id, student_no FROM student_info 填入 idMap for (int i 1; i sheet.getLastRowNum(); i) { Row row sheet.getRow(i); if (row null) continue; String studentNo row.getCell(0).getStringCellValue().trim(); Integer studentId idMap.get(studentNo); if (studentId null) { // 该学号不存在跳过并记录日志 continue; } // 用 studentId 插入 physical_exam }如果把装 Excel 的单元格误读成带小数点的数字比如1.232E9学号就会变成科学计数法。取学号前先判断单元格类型数字单元格用BigDecimal.valueOf(cell.getNumericCellValue()).toPlainString()再转字符串能省一堆麻烦。5. 避坑与排查从“连不上MySQL”到“导出Excel乱码”的五个真实雷区这章只讲我实际见过的问题。每个问题都按“现象 → 原因 → 解决”的顺序写你可以直接跳到对应条目对照排查。5.1 启动就报错驱动类、认证插件和时区三座大山报错一ClassNotFoundException: com.mysql.cj.jdbc.Driver现象登录窗口还没弹出来控制台直接抛出ClassNotFoundException并且指向DBUtil.getConnection()里的Class.forName那一行。原因MySQL 驱动 jar 不在运行 classpath 里。Maven 项目里通常是依赖没下载或 IDEA 的 Maven 面板没有刷新手动导入的项目则可能是把 jar 放进 lib 目录后忘了 Add as Library。解决先看external libraries里有没有mysql-connector-java。没有就重新mvn clean install手动模式在 IDEA 里选中 lib 目录右键 Add as Library。如果公司用的 Maven 私服缺这个坐标直接换源或下载 jar 手动引入。报错二Public Key Retrieval is not allowed 或时区The server time zone value ...现象能走到DriverManager.getConnection但连接建立失败报错关键词是Public Key Retrieval is not allowed或serverTimezone。原因MySQL 8 默认认证插件是caching_sha2_password第一次握手时客户端需要向服务器要公钥而 JDBC URL 里没开allowPublicKeyRetrieval时区报错则是因为 8.0 驱动要求显式指定时区。解决重写 JDBC URL把allowPublicKeyRetrievaltrueuseSSLfalseserverTimezoneAsia/Shanghai全部带上。这三项不是玄学是 MySQL 8 对接桌面程序的标准三件套。5.2 中文乱码界面、数据库和Excel三个环节一起查现象登录窗口输入中文查询结果显示成“”或者导出 Excel 后用 WPS 打开姓名全是乱码。原因字符集在某个环节断掉了。数据库表是 utf8但 JDBC URL 没带characterEncodingutf8导致 Java 侧发出 GBK 编码或连接层用了默认值另一种情况是 Excel 文件本身是 GBK 编码而代码按 UTF-8 读。解决三处必须一致。数据库建库时DEFAULT CHARACTER SET utf8mb4JDBC URL 里带useUnicodetruecharacterEncodingutf8Excel 读取用WorkbookFactory.create(file)时POI 会尽量识别文件自带的编码但如果 CSV 文件是 GBK就别用 Excel 读取器直接按Charset.forName(GBK)读文本。改完这三处还乱码重启一下 MySQL 客户端连接旧连接不会自动切换字符集。5.3 日期显示成数字或时间戳表格模型没做格式化现象JTable 里体检日期显示成1698768000000这种长数字或者导出的 Excel 日期列变成一坨数字。原因Swing 的DefaultTableModel默认把java.sql.Date当 Object 显示没有调用toString()格式化POI 写入 Date 类型时如果没指定单元格样式Excel 也会显示成序列号。解决插入表格模型前转换成字符串这是最简单也最稳的方式String dateStr rs.getDate(check_date) ! null ? new java.text.SimpleDateFormat(yyyy-MM-dd).format(rs.getDate(check_date)) : ;导出 Excel 时同样先把日期格式化成字符串再写入单元格虽然会损失 Excel 的原生日期属性但对课设和数据核对来说可读性优先。5.4 多选删除后自增ID跳号别强行回填主键现象用DELETE删掉几条体检记录后再插入数据发现id从 100 跳到 103中间缺了几个数觉得数据“乱”了。原因AUTO_INCREMENT只保证唯一不保证连续。删除过的 ID 不会重新分配这是 MySQL 的正常行为。对业务没有任何影响因为主键只做唯一标识不做计数。解决不要试图把 ID 回填成连续数字。如果你在答辩时被问到直接说“自增主键的连续性不影响业务且回填会造成主键冲突风险”这是加分回答。真正要查的是删除是否按主键定位而不是按行号定位。5.5 导入Excel时数据错位跳过表头却把空行也算进去了现象用 POI 导入 Excel结果第一条数据少了一列或者最后一行插进去的全是 null。原因sheet.getLastRowNum()返回的是最后一行索引但中间可能有完全空的行POI 不会自动跳过。另外如果第一行是表头循环应该从 1 开始如果表的标题占了两行从 1 开始仍会错位。解决用Row row sheet.getRow(i); if (row null) continue;跳过空行。表头行数不确定时先读前几行确认或者直接把第 0 行换成固定模板样式代码按模板列顺序取。更稳妥的做法是导入前在 Excel 里先做一次数据清洗把多余的标题行删掉。6. 把健康数据用起来动态折线图的统计SQL与验证技巧资源包里的动态折线图数据分析.png是这套系统的加分项它把体检数据从“能查能用”提升到“能看趋势”。先看统计查询怎么写。要画“平均身高随月份变化”的折线SQL 要对体检日期按月分组SELECT DATE_FORMAT(check_date, %Y-%m) AS month, AVG(height) AS avg_height, AVG(weight) AS avg_weight FROM physical_exam GROUP BY DATE_FORMAT(check_date, %Y-%m) ORDER BY month;如果只想看某一学年加 WHERE 条件过滤区间。如果学校想在同一个图里看男生和女生的差异加上s.gender字段按性别 月份双分组。Swing 端画图常见的做法是引入 JFreeChart把上面 SQL 的结果集转成折线图数据集TimeSeries series new TimeSeries(平均身高); while (rs.next()) { series.add(new Month( rs.getString(month)), rs.getDouble(avg_height)); } XYSeriesCollection dataset new XYSeriesCollection(series); JFreeChart chart ChartFactory.createCreateXYLineChart( 身高月度走势, 月份, 身高(cm), dataset); ChartFrame frame new ChartFrame(体检数据分析, chart); frame.pack(); frame.setVisible(true);这段代码的关键是Month对象使用字符串月份JFreeChart 会正确解析yyyy-MM格式。如果你显示出来 X 轴顺序错乱多半是 SQL 里没有ORDER BY month。图表缩放、颜色这些问题不影响功能但答辩时会被截图作为“数据分析”的证据。这里说一个我自己的血泪经验有一次帮别人复查这个系统发现界面上生成的折线图和数据库里的实际数据对不上排查了一下午最后定位到图表的TimeSeries数据竟然是读的data.xls里的一份静态 Excel而不是实时查库。改完数据库后图纹丝不动答辩时老师用新数据一验证当场翻车。从那以后我每次接手这类带图表的课设项目都会强制自己在源码里搜一遍setDataSource和FileInput确认所有的“数据分析”结果都来自 MySQL 查询并且每次生成图表前先联机刷一次。这个习惯帮我避过好几次类似的坑希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?