尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java学生信息管理系统课设:JDBC连接Access与DAO事务设计要点

Java学生信息管理系统课设:JDBC连接Access与DAO事务设计要点 简介一份面向Java程序设计专业学生的课程设计报告范例以“学生信息管理系统”为实战项目完整覆盖从任务书、需求分析、数据库设计到系统实施、测试验收的课程设计全流程适合需要完成Java面向对象课程设计或撰写设计报告的读者参考。资源为单个Word文档压缩包总大小252KB内容包含系统功能模块说明学生/教师信息增删改查、JDBC连接Access数据库的实现思路以及Eclipse开发工具的实践细节可作为文档撰写与项目开发的直接模板。目前已有206人浏览学习。通过阅读这份报告读者能快速了解课程设计报告的目录组织方式、开发计划与进度安排并获取学生信息管理系统的整体设计方案对提升面向对象编程实践能力和报告写作规范具有一定参考价值。1. 学生信息管理系统课设Java 面向对象与 JDBC 落地时最容易被忽略的三件事每年 Java 课程设计都会出现一批“学生信息管理系统”这份报告也不例外Eclipse Java Neon 3 Swing JDBC Access实现了学生、教师、成绩三组信息的增删改查。看起来是课设入门题但真把代码从教室搬到答辩机器上一半人会栽在驱动加载、中文乱码和密码明文这三件事上。原因很简单课设题目要求的是“能运行”而实际工程要求的是“能换环境运行”。这篇内容以该课程设计报告为骨架把表结构设计、JDBC 连接、登录认证、CRUD 事务、验收排错逐层拆开适合正在写 Java 课设的人也适合需要带实习生走完一个完整桌面项目的工程师。2. 数据库表设计与 JDBC 驱动选择Access 课设的边界与坑2.1 从数据字典看懂五张表的实体关系这份报告里的数据库名为 student1核心表有班级表、学生表、教师表、课程表、成绩表。从数据字典可以还原出接近第三范式的关系模型学生通过班级编号关联班级成绩表用学号、课程号、教师编号三个外键把学生、课程、教师串起来。表名主键外键关键字段班级表班级编号无班级名称、所属学院、班级人数学生表学号班级编号姓名、性别、出生日期教师表教师编号无姓名、性别、职称、所属学院课程表课程号无课程名、学分成绩表学号 课程号学号、课程号、教师编号成绩这里有一个值得注意的细节报告中文档写了“成绩表外键参照课程表中课程编号”而课程表主键是“课程号”。字段命名不一致在课设报告里很常见但在代码里会直接导致 SQL 连表查询时列名对不上。规范做法是主键和外键保持同名比如统一叫 course_id避免 JOIN 时出现歧义列。另外班级表引入后学生信息里就不必重复存学院名查询时需要班级表和学院表做关联。这种通过外键拆表的设计正是面向对象思想在数据层的映射把重复信息提取成独立实体而不是在每个学生记录里冗余一份。2.2 JDBC 连接 AccessODBC 桥与 UCanAccess 的取舍报告里写了“通过 JDBC 访问数据库”但 Access 不是服务型数据库JDBC 不能直连必须通过驱动转换。早期课程设计常用 JDBC-ODBC 桥配置数据源、写 jdbc:odbc:student1 就能连。但 JDK 8 开始官方移除了 JDBC-ODBC 桥Oracle 也早就把它标记为 deprecated。现在新写 Java 8 以上程序再用这种连接方式会直接报 ClassNotFoundException。常见替代方案是开源的 UCanAccess它是一个纯 Java 驱动不需要配 ODBC 数据源兼容 .mdb 和 .accdb 格式。import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { // 使用 UCanAccess 驱动连接 Access 数据库 private static final String DB_URL jdbc:ucanaccess://D:/student1.accdb; private static final String USER ; private static final String PASSWORD ; public static Connection getConnection() throws SQLException { Connection conn null; if (conn null || conn.isClosed()) { conn DriverManager.getConnection(DB_URL, USER, PASSWORD); } return conn; } }这段代码的要点在于驱动全类名 net.ucanaccess.jdbc.UcanaccessDriver 必须出现在依赖里。如果用 Maven需要在 pom.xml 里加入 ucanaccess 依赖否则 Class.forName 会失败。DB_URL 里指定的是磁盘上的绝对路径路径中不能出现中文目录否则 UCanAccess 在部分 Windows 环境下会抛 IOException。提示UCanAccess 3.0.9 之后依赖 jackcess 和 hsqldb导入项目时要一并引入否则连接时会报 NoClassDefFoundError。2.3 连接参数与中文乱码的关系Access 驱动本身默认使用平台字符集Windows 中文版下通常是 GBK而 Java 源码和 Swing 组件默认是 UTF-16 内部表示。如果不做处理插入中文数据后再读出来容易变成问号。常见做法是在获得 Connection 后执行一次conn.createStatement().execute(SET DATABASE COLLATION IGNORECASE)或者干脆在表单层统一录入和显示都走 String由 JDBC 驱动负责编码转换。UCanAccess 在多数情况下能正确处理 Unicode但要注意 .accdb 文件本身是否包含旧版编码的备注字段。调试时先在 Access 里手动输一条中文记录再用同一个驱动读出来可以快速定位是写入问题还是读取问题。3. 登录模块与角色权限ComboBox 之外的认证细节3.1 登录界面的 Swing 组件组织报告里登录模块的设计构想是用 JLabel 提示身份选择用 JComboBox 选择学生、教师、管理员用 JTextField 输入用户名用 JPasswordField 输入密码。Swing 组件本身不难难的是事件监听和窗口跳转。JComboBoxString roleBox new JComboBox(); roleBox.addItem(学生); roleBox.addItem(教师); roleBox.addItem(管理员); JTextField userField new JTextField(15); JPasswordField passField new JPasswordField(15); JButton loginBtn new JButton(登录); loginBtn.addActionListener(e - doLogin(roleBox, userField, passField));登录按钮触发的 doLogin 方法里需要从 JComboBox 拿角色、从 JTextField 拿账号、从 JPasswordField 拿密码。注意 JPasswordField 的 getText() 已过时应该用new String(passField.getPassword())拿到后立刻用完置空避免 char 数组残留密码。这个细节在检查代码时很加分因为它属于 Java 安全基础而不是功能逻辑。3.2 从明文密码到加盐哈希报告里登录模块没有明确说明密码存储方式但课设报告中大量截图里能看出密码直接和用户名一起写在数据库表里。只做功能演示没问题但既然面向对象课程强调“设计”密码字段就值得单独处理。常见做法是使用 MD5 加盐配合 Base64 存储不存明文。import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; public class PasswordUtil { // 盐值可以和用户名拼接也可以随机生成后入库 public static String encode(String rawPassword, String salt) { String input salt rawPassword; try { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(input.getBytes(UTF-8)); return Base64.getEncoder().encodeToString(digest); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(MD5 算法不可用, e); } catch (Exception e) { throw new RuntimeException(密码编码失败, e); } } }这里用 Base64 输出是为了让密文长度固定且适合存 varchar 字段。盐值一般取用户名的反向加固定串或者登录时从用户表里读取每行独立的随机盐。MD5 本身不再适合抗碰撞但课设场景里已经足够说明“不存明文”这个意识。真要在生产环境做建议换成 SHA-256 加随机盐或者直接走 BCrypt。3.3 角色路由与权限控制登录成功后要做的不是直接打开主窗口而是根据角色决定能进入哪些模块。报告里的功能模块图已经画出管理员、教师、学生三类权限。功能模块管理员教师学生学生信息增删改是否否教师信息增删改是否否成绩录入修改是是否成绩查询是是是用户权限管理是否否实现时不要在主窗口里用 if 判断一堆按钮是否可见更简洁的方式是登录成功后拿到一个 Role 枚举然后用策略模式构建主窗体。比如定义enum Role { STUDENT, TEACHER, ADMIN }再写一个实现了 WindowBuilder 接口的工厂类根据角色返回不同面板。这样把角色选择从界面代码里剥离出来后续加权限点只需要改工厂不用动每一个窗口。4. 学生/教师/成绩 CRUDDAO 模式与事务边界4.1 为什么课设代码里最缺 DAO 层报告里的文件组织结构相当朴素很多方法直接写在 JFrame 里按钮事件里写 SQL结果集直接往 JTable 上填。这样做能跑但学生五张表之间还有外键关联一旦班级表调整所有窗口里的 SQL 都要跟着改。面向对象设计强调职责分离放在这个场景里最直接的手段就是引入 DAO 层。DAO 模式把数据库访问拆成接口和实现。界面层只依赖 DAO 接口不关心 SQL 是写给 Access 还是 MySQL。这样登录模块调用 UserDao学生窗口调用 StudentDao成绩窗口调用 ScoreDao每个 DAO 只负责一张或两张核心表的持久化逻辑。4.2 一个标准 DAO 方法的写法以学生表为例添加学生时要同时校验班级编号是否存在。先看一个最小骨架的 StudentDaopublic class StudentDao { private Connection conn; public StudentDao(Connection conn) { this.conn conn; } // 添加学生成功返回 true public boolean addStudent(Student student) throws SQLException { String sql INSERT INTO 学生表(学号,姓名,性别,出生日期,班级编号) VALUES(?,?,?,?,?); try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, student.getId()); ps.setString(2, student.getName()); ps.setString(3, student.getGender()); ps.setDate(4, new java.sql.Date(student.getBirthday().getTime())); ps.setString(5, student.getClassId()); return ps.executeUpdate() 1; } } }PreparedStatement 有两层作用一是占位符避免 SQL 拼接导致的注入问题二是在 Access 下能预编译重复执行的 SQL减少转义错误。注意出生日期字段传入参数时要把 java.util.Date 转成 java.sql.Date否则 JDBC 驱动会报类型不匹配。getTime() 取出毫秒值后构建 sql.Date 是最稳写方式。另一个规范是使用 try-with-resources。Connection 由外部传入PreparedStatement 和 ResultSet 在这个 try 里自动关闭避免资源泄漏。很多课设代码忘关 ResultSet在 Access 单机场景看不出问题但如果以后把数据库换成 MySQL连接池很快会被占满。4.3 成绩表联合查询与事务成绩表是五张表里外键最多的表录入成绩时需要同时确认学生存在、课程存在、教师存在。单独 INSERT 不足以保证数据一致性比如一次期中考试要录入全班 30 条成绩记录中间某一条学号填错前面的插入就白白执行了。常见处理方式是开启事务全部成功再提交。public void saveScoreBatch(ListScore scores) throws SQLException { String checkStudent SELECT COUNT(*) FROM 学生表 WHERE 学号?; String insertScore INSERT INTO 成绩表(学号,课程号,成绩,教师编号) VALUES(?,?,?,?); try (PreparedStatement psCheck conn.prepareStatement(checkStudent); PreparedStatement psInsert conn.prepareStatement(insertScore)) { conn.setAutoCommit(false); for (Score s : scores) { psCheck.setString(1, s.getStudentId()); try (ResultSet rs psCheck.executeQuery()) { if (!rs.next() || rs.getInt(1) 0) { throw new IllegalArgumentException(学号不存在: s.getStudentId()); } } psInsert.setString(1, s.getStudentId()); psInsert.setString(2, s.getCourseId()); psInsert.setInt(3, s.getScore()); psInsert.setString(4, s.getTeacherId()); psInsert.addBatch(); } psInsert.executeBatch(); conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); } }这个批次方法的关键是先关掉自动提交然后循环里边校验边批量插入。addBatch 与 executeBatch 配合能显著减少对 Access 的往返调用。rollback 必须放在 catch 里否则一条坏数据会让后面所有记录一起丢失。事务边界放在业务方法内、连接从外部传入是为了让调用方决定是要 commit 还是 rollback这样成绩导入界面和普通单条录入界面可以复用同一个 DAO。提示Access 的 Jet 引擎对事务粒度有限制过大的批量操作建议每 100 条 commit 一次否则可能出现“内存不足”的驱动错误。5. 结课验收前的排错清单乱码、驱动和日期5.1 中文乱码三板斧Swing 界面中文乱码和 Access 数据中文乱码是两个不同问题。界面乱码时先检查 Eclipse 的 workspace 编码是否为 UTF-8再检查运行时的 JVM 参数是否加了 -Dfile.encodingUTF-8。java -Dfile.encodingUTF-8 -jar studentSystem.jar数据乱码则要从驱动层排查。UCanAccess 连接串默认按 UTF-8 处理字符流如果使用旧版 ODBC 桥需要先在控制面板里指定 Access 数据库的排序规则。最简单的验证方式在系统里新建一个测试表只写一条中文记录用不同驱动反复读取看到问号就说明是写路径出了问题而不是显示层问题。5.2 Access 驱动与 64 位 JVM 的经典冲突课程设计机器大多已经换成 64 位 JDK而 Office 安装的 Access 数据源驱动可能是 32 位。用 JDBC-ODBC 桥时位不匹配会导致 “getDriver” 报错很多同学以为是代码问题其实是本机缺少对应位数的 Microsoft Access Driver。换成 UCanAccess 后这个障碍被绕过了因为它是纯 Java 实现与 Office 的 ODBC 驱动没有依赖关系。5.3 日期字段往返的正确姿势Access 里日期字段会返回 java.sql.Timestamp如果 JTable 直接显示会带出毫秒尾巴。处理办法是在表格渲染器里统一格式化。SimpleDateFormat df new SimpleDateFormat(yyyy-MM-dd); String display df.format(rs.getTimestamp(出生日期));这个格式化只影响展示写回数据库时仍然要转成 java.sql.Timestamp。排序时如果直接对字符串排序会出现 1990-1-1 排在 1989-2-2 之前的错误必须用 Date 对象排序。答辩时主动说出这个坑比代码跑通更有说服力。最后一个验收技巧把项目复制到一台没有配置过任何环境变量的机器上从 JDK 安装开始完整跑一遍。能跑通才说明报告里写的“系统可读性好、易于维护”不是空话。本文还有配套的精品资源点击获取
返回列表