ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

企业工资管理系统Java课程设计:从ER模型到JDBC三层架构

企业工资管理系统Java课程设计:从ER模型到JDBC三层架构 简介面向数据库课程设计的《企业工资管理系统Java版完整代码》是一份完整的课程设计报告内容覆盖需求分析、总体设计、详细设计、系统实现与实验总结适合高校学生在完成数据库原理及应用课程设计时参考仿写。报告从员工信息、基本工资、津贴三类核心数据出发明确了基本工资与津贴设定、月工资计算、工资信息录入/添加/修改等主要功能并给出性能需求默认精度3位小数、响应时间不超过0.5秒、支持跨平台数据互通、数据流图、E-R图以及功能模块划分。详细设计部分列出了员工表、基本工资表、津贴信息表的字段结构与主键约束针对职工信息管理、工资管理、职工登录查询三个模块分别说明系统实现部分则配有界面截图和Java设计代码完整呈现了基于MySQL数据库的工资管理系统的落地过程。资源包内共1个doc文档大小约261KB内容精炼集中。目前已有766人学习对需要快速理解JavaMySQL数据库应用开发流程、完成课程设计报告的同学有较高参考价值。1. 数据库课程设计选企业工资管理系统到底值在哪很多人选课设题目时有个误区觉得名字越新潮越好电商秒杀、推荐系统、AI识别一股脑往上堆结果数据库设计撑不起来答辩时被老师问几句就露馅。企业工资管理系统看起来朴素却是把数据库增删改查、事务、多表查询、完整性约束这些考点全部串起来的标准场景几乎每个学期的优秀课设里都有它的身影。标题里“java版完整代码”意味着你不用从零造轮子重点是把成型的工程读懂、改成自己的再配合一篇能说清设计思路的课程设计说明书。这门课设适合计算机专业三、四年级学生也适合想补一块完整项目经验的初级开发者。把三张表和一套 JDBC 代码吃透比堆一堆炫技框架稳得多。2. 需求分析与表结构设计三张表怎么撑起一门课设工资管理系统听起来功能很多拆开看就是三件事管部门、管员工、管每个月发工资。很多人的问题不是表建得不够多而是关系理不清。最典型的反面案例是把所有字段塞进一张大表员工所属部门名在每一行里重复存储改一次部门名要 UPDATE 几十行这种设计在答辩时基本属于送人头。用第三范式把数据拆开再靠外键把关系重新连接起来才是课程设计该有的样子。2.1 为什么是“员工-部门-工资”三张表ER模型与范式先画 ER 图再做表这是顺序问题。ER 图里三个实体分别是部门、员工、工资记录关系也很清楚部门对员工是 1:N员工对工资记录是 1:N。翻译成表结构就是dept 表只存部门自身的信息部门ID、部门名称、负责人employee 表存员工固定属性工号、姓名、所属部门、岗位、基本工资、入职日期salary_record 表存每个月给每个员工发放的工资明细属于“每月发生一次”的事实数据。这里要特别说清楚为什么工资单独建表而不是加在员工表里。员工表里如果放了“本月工资”字段下个月发工资时只能覆盖上个月的数据历史记录全丢了。工资是时间维度的数据它的特点是按月产生一条必须独立成表通过 emp_id 关联员工。这个思路叫作“把固定信息和流水信息分开存储”说明书里写明白这一点老师会觉得你真的理解设计目标而不是在堆表。范式层面这套设计满足第三范式每个非主键字段都直接依赖主键不产生传递依赖。员工表通过 dept_id 关联部门部门名称没有冗余到员工表工资表只存 emp_id员工姓名也要通过关联去查。实际课设里不需要刻意强调“我用了第几范式”但被问到时你要能指出明明是有意识做的设计不是歪打正着。2.2 建库建表SQL字段类型、默认值与外键约束建库脚本是整套代码的地基。我用 MySQL 8.X 写了一份可以直接跑的 DDL字符集用 utf8mb4避免测试数据里出现生僻字时插入报错。CREATE DATABASE salary_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE salary_db; CREATE TABLE dept ( dept_id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT 部门ID, dept_name VARCHAR(50) NOT NULL UNIQUE COMMENT 部门名称, manager VARCHAR(20) DEFAULT NULL COMMENT 部门负责人 ) ENGINEInnoDB COMMENT部门表; CREATE TABLE employee ( emp_id VARCHAR(10) PRIMARY KEY COMMENT 工号例如 E001, emp_name VARCHAR(50) NOT NULL COMMENT 姓名, dept_id TINYINT UNSIGNED NOT NULL COMMENT 所属部门ID关联dept表, position VARCHAR(50) NOT NULL DEFAULT 普通员工 COMMENT 岗位名称, base_salary DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 基本工资, hire_date DATE NOT NULL COMMENT 入职日期, status TINYINT NOT NULL DEFAULT 1 COMMENT 1在职 0离职, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(dept_id) ) ENGINEInnoDB COMMENT员工表; CREATE TABLE salary_record ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, emp_id VARCHAR(10) NOT NULL COMMENT 员工工号, salary_month CHAR(7) NOT NULL COMMENT 工资月份格式2025-01, base_salary DECIMAL(10,2) NOT NULL COMMENT 基本工资, position_salary DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 岗位工资, bonus DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 奖金, deduction DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 扣款合计, real_salary DECIMAL(10,2) NOT NULL COMMENT 实发工资, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 发放时间, CONSTRAINT fk_salary_emp FOREIGN KEY (emp_id) REFERENCES employee(emp_id), UNIQUE KEY uk_emp_month (emp_id, salary_month) ) ENGINEInnoDB COMMENT工资发放记录表;这份表结构里每个选择都有理由答辩被问到时不能只说“别人都这么建”。salary_month 用 CHAR(7) 而不是 DATE因为工资月份只有年和月没有日DATE 类型反而会引入“2025-01-00”这种非法日期问题且 CHAR(7) 定长存储配合 LIKE 2025-% 做月份范围查询效率更高。金额字段全部用 DECIMAL(10,2)10 位有效数字其中 2 位小数这正好能支撑千万级累计金额别用 FLOAT二进制浮点数存钱会出精度问题后面避坑章专门讲。工资表上的 UNIQUE KEY uk_emp_month 是业务规则的体现一个员工同一个月只能有一条工资记录。这个唯一索引比在 Java 代码里先查后插可靠得多因为它是在数据库层面兜底哪怕两个人同时点击“发放工资”数据库也只会允许一条插入成功。外键默认是 RESTRICT 策略删除有工资记录的员工时会直接报错这是保护历史数据的安全机制先让员工离职status 置 0而不是物理删除。2.3 索引与约束三个值得写进说明书的设计细节建表只是第一步说明书里如果能写出下面三个细节档次立刻不一样。第一个细节是外键的删除策略。ON DELETE CASCADE 看着方便删部门连带删员工但演示时一旦误删整批数据全没了这种设计在真实系统里属于高危操作。课设推荐保留默认的 RESTRICT并且在说明书里写明离职员工的信息要在业务层面保留不物理删除工资历史才能追溯。一句话解释“我为什么不用级联删除”老师就知道你考虑过数据安全。第二个细节是唯一索引承担了类似锁的防重职责。多线程场景下真实系统发工资要考虑数据库并发锁防止同一员工被重复发放课设不需要真的去演示锁冲突但可以在数据库层用唯一索引等效实现这个约束。在说明书的“创新点”里写“通过数据库唯一索引代替应用层判重减少一次查询”比空谈并发更实在。第三个细节是 salary_month 字段要建普通索引。工资系统最常见的查询是“查某个月所有人的工资”和“查某个人所有月份的工资”前者会反复用到 salary_month 作为 WHERE 条件。没有索引时全表扫描数据量到几万条后明显卡顿加了索引后查询走 B 树速度是数量级提升。索引不是越多越好每个索引都会拖慢插入速度工资表只在 emp_id 和 salary_month 上建立必要索引就够了。注意如果你要在老版本 MySQL 上跑建库语句里的 utf8mb4 需要 MySQL 5.5 以上才支持5.1 之前只能用 utf8。现在课程设计基本都用 8.X这个问题遇到再处理。3. 用 JDBC 三层架构落地 Java 源码从 DBUtil 到 SalaryDAO表结构定好之后Java 代码怎么组织是第二个关键决策。我见过不少同学用 Swing 写了一坨上千行的类界面逻辑和 SQL 混在一起跑是能跑答辩时根本讲不清。课程设计不是软件工程课设但也至少要表现出基本的“分层意识”。这里的常见做法是 JDBC 三层架构实体类对应表DAO 层只做增删改查Service 层写业务规则UI 层负责和用户交互。3.1 三层结构怎么切entity、dao、service、ui 的类规划为什么不推荐在这个课设里直接上 Spring Boot因为数据库课程设计的评分点恰恰是 SQL 和 JDBC 基本功Spring Boot 把连接管理、事务、参数绑定全部封装掉了代码里看不到 PreparedStatement也讲不出连接关闭的时机。用原生 JDBC 写一遍你对数据库操作的理解深度完全不一样。框架可以放在“改进方向”里提一句而不是作为主线。常见的包结构是这样的包名职责典型类entity与三张表一一对应的实体类字段与表字段同名Employee、Dept、SalaryRecorddao数据访问只写 SQL 和 JDBC 代码不写业务判断EmployeeDao、SalaryDaoservice业务逻辑组合 DAO 完成工资计算、发放、汇总SalaryServiceui控制台菜单或 Swing 窗体只做输入输出MainFrame、LoginDialog这个分层的核心思想是“改动一处不连累其他处”。比如数据库从 MySQL 换成 Oracle只需要改 DBUtil 和 DAO 里的 SQL界面完全不动比如工资计算公式变了只改 Service 层DAO 不需要知道。答辩时你可以直接指着一行代码说这里是 DAO它只负责和数据库打交道这里是 Service业务规则都沉淀在这一层。这就是面向对象编程 java 落地时最朴素也最好用的组织方式。3.2 DBUtil 连接工具类db.properties 配置与连接池参数不管哪个 DAO 都要拿数据库连接这个动作必须抽出来。用配置文件而不是把连接串硬编码在代码里换数据库环境时不需要重新编译这也是课设的加分项。package util; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; public class DBUtil { private static String driver; private static String url; private static String user; private static String password; static { try (InputStream in DBUtil.class.getResourceAsStream(/db.properties)) { Properties props new Properties(); props.load(in); driver props.getProperty(jdbc.driver); url props.getProperty(jdbc.url); user props.getProperty(jdbc.user); password props.getProperty(jdbc.password); Class.forName(driver); } catch (Exception e) { throw new ExceptionInInitializerError(数据库配置初始化失败请检查 db.properties); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } }配套的 db.properties 放在 src 根目录下jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/salary_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.userroot jdbc.password123456说明几个关键参数。jdbc.url 里 useUnicode 和 characterEncoding 是中文不乱码的前提编码必须和建库时的 utf8mb4 一致。serverTimezone 是 MySQL 8.X 必须加的参数不加会报时区错误。useSSLfalse 是本地开发关闭 SSL 加密避免证书告警。password 是数据库密码演示前要确认和本机一致。Class.forName 的作用是触发驱动类的静态初始化把驱动注册到 DriverManagerMySQL 8.X 的驱动类名是 com.mysql.cj.jdbc.Driver老写法 com.mysql.jdbc.Driver 在新驱动里已经不存在了。这个工具类能直接跑通但它是最简单的 DriverManager 直连模式每次 getConnection 都会新建物理连接。课设阶段并发量低这么做没问题如果你在说明书里写“后续可以引入连接池”建议用 HikariCP并在连接池配置里把 maximumPoolSize 控制在 10 以内因为 MySQL 默认最大连接数是 151连接池开太大反而把自己搞挂。3.3 DAO 层写法PreparedStatement 完成增删改查避免 SQL 注入DAO 层最常见的错误是拿字符串拼 SQL。比如SELECT * FROM employee WHERE emp_id id 这种写法一旦用户输入E001 OR 11查询条件就变成恒真整张表被拖出来。JDBC 提供 PreparedStatement 解决的就是这个问题参数用问号占位由驱动完成转义和预编译。package dao; import entity.Employee; import util.DBUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class EmployeeDao { public boolean insertEmployee(Employee e) { String sql INSERT INTO employee (emp_id, emp_name, dept_id, position, base_salary, hire_date) VALUES (?,?,?,?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, e.getEmpId()); ps.setString(2, e.getEmpName()); ps.setInt(3, e.getDeptId()); ps.setString(4, e.getPosition()); ps.setBigDecimal(5, e.getBaseSalary()); ps.setDate(6, new java.sql.Date(e.getHireDate().getTime())); return ps.executeUpdate() 1; } catch (SQLException ex) { ex.printStackTrace(); return false; } } public Employee findById(String empId) { String sql SELECT emp_id, emp_name, dept_id, position, base_salary, hire_date FROM employee WHERE emp_id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, empId); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { Employee e new Employee(); e.setEmpId(rs.getString(emp_id)); e.setEmpName(rs.getString(emp_name)); e.setDeptId(rs.getInt(dept_id)); e.setPosition(rs.getString(position)); e.setBaseSalary(rs.getBigDecimal(base_salary)); e.setHireDate(rs.getDate(hire_date)); return e; } } } catch (SQLException ex) { ex.printStackTrace(); } return null; } }这里 try-with-resources 是 Java 7 以后的写法Connection、PreparedStatement、ResultSet 都实现了 AutoCloseable代码块结束后自动关闭顺序是先关 ResultSet再关 Statement最后关 Connection。很多人课设里的“连接泄漏”就是忘了在 finally 里关闭资源用这个写法可以从机制上避免。要注意实体类里的 BigDecimal 字段和数据库的 DECIMAL 对应用 rs.getBigDecimal 读取不要图省事先 getString 再转容易出现精度损失。如果工资表用自增主键插入后要拿到新 ID通常在 prepareStatement 时加一个参数Statement.RETURN_GENERATED_KEYS再用 getGeneratedKeys() 获取这在员工表用自增 ID 的场景下会用到。4. 工资计算、部门汇总与防重复发放业务逻辑的正确打开方式表结构和 DAO 准备齐全后工资系统的“灵魂”在 Service 层。这里包括工资怎么算、部门怎么汇总、发放时怎么保证不重复、不半途失败。很多课设代码能跑通但业务规则散落在界面事件里改一个公式要找半天这节就讲清楚怎么把业务逻辑做扎实。4.1 工资计算公式放 service 层怎么算实发、怎么校验工资的计算公式可以定成实发工资 基本工资 岗位工资 奖金 - 扣款合计。扣款合计里包含请假扣款、迟到扣款、五险一金个人部分。计算本身不复杂复杂的是三个绑定的原则用 BigDecimal 计算、校验结果合法性、不把公式写进 DAO 或 UI。package service; import entity.SalaryRecord; import java.math.BigDecimal; public class SalaryService { public BigDecimal calcRealSalary(SalaryRecord r) { BigDecimal real r.getBaseSalary() .add(r.getPositionSalary()) .add(r.getBonus()) .subtract(r.getDeduction()); if (real.compareTo(BigDecimal.ZERO) 0) { throw new IllegalArgumentException(实发工资不能为负数请检查扣款数据); } if (real.compareTo(new BigDecimal(1000000.00)) 0) { throw new IllegalArgumentException(实发工资超过百万上限疑似数据录入错误); } return real.setScale(2, BigDecimal.ROUND_HALF_UP); } }这里使用 BigDecimal 而不是 double原因是二进制浮点数没法精确表示 0.1多次累加后会出现 0.30000000000000004 这种结果工资差几分钱对不上账在答辩演示时非常尴尬。setScale(2, ROUND_HALF_UP) 表示保留两位小数四舍五入。校验分两端实发为负直接抛异常这是最常见的脏数据来源比如录入扣款时把三位数当成两位数超过百万的校验是我个人习惯防止测试时手滑多敲两个零。业务校验放在 Service 层而不是 DAO 层因为 DAO 的职责是存取数据不知道“工资不能为负”这条业务规则。4.2 按部门汇总与按月查询一条 GROUP BY 让答辩有话说课程设计说明书里如果只有增删改查评语往往是“功能完整但缺乏亮点”。加一个“按部门查询月度工资汇总”功能SQL 的含金量立刻不一样因为它涉及多表连接、分组聚合和结果过滤三个考点。SELECT d.dept_name, COUNT(s.id) AS emp_count, SUM(s.base_salary) AS total_base, SUM(s.position_salary) AS total_position, SUM(s.bonus) AS total_bonus, SUM(s.deduction) AS total_deduction, SUM(s.real_salary) AS total_real FROM salary_record s JOIN employee e ON s.emp_id e.emp_id JOIN dept d ON e.dept_id d.dept_id WHERE s.salary_month 2025-01 GROUP BY d.dept_id, d.dept_name HAVING SUM(s.real_salary) 0 ORDER BY total_real DESC;这条 SQL 里有三个答辩高频考点。第一是 JOIN 的写法工资表关联员工表拿到 dept_id再关联部门表拿到部门名称而不是在工资表里冗余存一个部门名字段。第二是 GROUP BY 与 SELECT 的约束select 后面的非聚合列dept_name必须出现在 GROUP BY 里MySQL 在默认 sql_mode 下对这项检查比较宽松但换到严格模式会直接报错这也是很多人在老环境能跑、新环境报错的原因。第三是 WHERE 和 HAVING 的分工WHERE 在分组之前过滤行HAVING 在分组之后过滤组。这里“工资月份等于某月”属于行级过滤放 WHERE如果要求过滤掉总金额小于某个值的部门就得用 HAVING。两者不能互换。在 Java 里执行这条查询时结果集的处理方式和普通查询不同每组返回一行需要封装成一个汇总实体或 Map。我一般会建一个 DeptSalarySummary 类字段和查询列一一对应然后循环 ResultSet 填充。4.3 防重复发放事务 唯一索引双保险发工资不是单条插入而是“给所有在职员工一次性生成当月记录”。真实场景下最怕的情况是老板点击“发放 2025-01 工资”程序跑了 10 条第 11 条因为某种原因失败前 10 条却留在数据库里。数据库事务专门解决这类问题要么全部提交要么全部回滚。public void paySalaryForMonth(String salaryMonth) { String sql INSERT INTO salary_record (emp_id, salary_month, base_salary, position_salary, bonus, deduction, real_salary) VALUES (?,?,?,?,?,?,?); try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement ps conn.prepareStatement(sql)) { for (Employee e : employeeDao.findAllActive()) { SalaryRecord r buildRecord(e, salaryMonth); ps.setString(1, e.getEmpId()); ps.setString(2, salaryMonth); ps.setBigDecimal(3, r.getBaseSalary()); ps.setBigDecimal(4, r.getPositionSalary()); ps.setBigDecimal(5, r.getBonus()); ps.setBigDecimal(6, r.getDeduction()); ps.setBigDecimal(7, r.getRealSalary()); ps.addBatch(); } ps.executeBatch(); conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } } catch (SQLException e) { e.printStackTrace(); } }setAutoCommit(false) 关闭自动提交之后所有 SQL 都在同一个事务里执行中间任何一步抛异常rollback 会把整个批次撤销不会留下“发了一半工资”的脏数据。executeBatch() 把多条 insert 打包发给 MySQL减少网络往返数据量大时性能提升明显。事务保证的是原子性唯一索引保证的是幂等性。假设程序没做检查同一个月连续点了两次“发放”第一次成功插入 100 条第二次执行到第一条就撞上唯一索引报错整个事务回滚不会出现重复数据。如果只靠 Java 代码里先SELECT COUNT(*)再决定是否插入在并发场景下会存在检查与插入之间的空窗期两次请求可能同时通过检查最后插入两条重复记录。数据库层的唯一索引是最后一道防线这也呼应了数据库并发锁那一类问题在工程上的常见解法能由数据库约束保证的不要完全依赖应用层判断。5. 避坑指南连不上库、中文乱码、精度丢失等高频翻车点课设翻车的高峰期不是写代码的时候而是答辩前一晚部署到老师电脑上的时候。以下五个问题我在辅导课设时见得太频繁每一条都是“现象 → 原因 → 解决”的完整链路提前踩一遍答辩时就不慌。5.1 驱动加载失败ClassNotFoundException 与 MySQL 时区报错现象程序启动报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或者能加载驱动但连接时报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。原因MySQL 8.X 之后驱动类名改成了com.mysql.cj.jdbc.Driver老名字只在 5.X 驱动里存在。而时区报错是 MySQL 8.X 的新要求连接串里必须显式声明 serverTimezone。解决驱动类名改成com.mysql.cj.jdbc.Driverurl 后面拼上serverTimezoneAsia/Shanghai。如果老师机器上装的是 MySQL 5.7com.mysql.jdbc.Driver还能用但建议统一按 8.X 改向下兼容没问题。这个报错最常见的原因是资料包里的 db.properties 是几年前写的拿到后第一件事就是检查这两处。5.2 中文乱码控制台问号和数据库里的乱码现象从控制台插入中文查询出来是“”或者直接报Incorrect string value: \xE5\xBC\xA0...。原因字符集不统一。数据库是 utf8mb4连接串没指定 characterEncoding客户端控制台是 GBK三处只要有一处不一致就会乱码。解决三处对齐。建库时指定DEFAULT CHARACTER SET utf8mb4这个在前面建表语句里已经做了连接串加useUnicodetruecharacterEncodingutf8mb4IDE 控制台编码改成 UTF-8IDEA 在 Settings → Editor → File Encodings 里把 Global Encoding 和 Project Encoding 都改成 UTF-8。还有一个隐蔽坑db.properties 文件本身如果用 Windows 记事本编辑另存为 GBK里面的中文注释也会坏建议用 IDE 编辑 properties 文件。5.3 连接没关运行一会报 Too many connections现象程序跑了几次操作后突然报Too many connections重启项目又好了过一会儿又犯。原因DAO 里获取了 Connection 但没关闭。DriverManager 直连模式下每次 getConnection 都会建立新的物理连接MySQL 默认最大连接数 151连接不释放很快耗尽。还有一种情况是引入了连接池但 maximumPoolSize 设得过大比如 HikariCP 默认 10但如果同时在跑多个项目实例也会顶爆上限。解决所有 JDBC 操作改成 try-with-resources确保 Connection、Statement、ResultSet 在异常时也能关闭。排查时先执行SHOW PROCESSLIST;看有没有大量 Sleep 状态的连接有就说明代码里有连接没关。如果课设里确实想用连接池把 maximumPoolSize 调到 5 到 10并让连接池管理连接的生命周期而不是每次都新建。5.4 工资算错浮点数精度翻车现象实发工资计算结果显示 12345.670000000002 这种尾巴或者几条工资加起来总和和计算器对不上差几分钱。原因Java 的 double 和数据库的 FLOAT 都基于 IEEE 754 二进制浮点数0.1 在二进制里是无限循环小数存储时被截断运算误差累积后就暴露了。工资这种金额数据根本不能用浮点类型承载。解决数据库金额字段全部用 DECIMAL(10,2)Java 端实体类用 java.math.BigDecimalDAO 读取用 getBigDecimal计算用 add/subtract/multiply禁止用 double 转 BigDecimal尤其不要用new BigDecimal(0.1)要用BigDecimal.valueOf(0.1)或字符串构造。这个坑属于“平时不炸一演示就炸”的类型答辩前最好用一组带小数的测试数据手工验一遍。5.5 外键约束拦路删除部门报错删不掉现象删除部门表某条记录时报错Cannot delete or update a parent row: a foreign key constraint fails。原因employee 表还有员工引用这个 dept_id外键默认的 RESTRICT 策略不允许删除被引用的父记录。这是保护机制在工作不是代码 bug。解决分情况处理。如果要删的部门里没有员工直接删如果有要先把这个部门的员工转到其他部门UPDATE employee SET dept_id ?或者把员工标记为离职再处理。千万不要在演示时硬删也不建议为了省事把外键去了外键是数据库完整性约束的一部分课程设计的评分标准里明确有它。说明书里应写清楚“员工离职采用逻辑删除历史工资记录保留可查”这比物理删除更能体现工程意识。6. 答辩是最后一道坎三个加分改造与演示自检清单课程设计做到能跑只是及格答辩时让老师觉得“有设计、有思考”才是拿高分的关键。这里分享三个投入产出比最高的改造都是在现有 JDBC 三层架构上做加法不伤筋动骨。第一个是导出工资表到 Excel。利用 Apache POI 把按月份查询的结果集写成 .xls 文件代码量不大但在演示时非常直观。老师可能只问一句“能不能导出”这个功能一展示整条链路就从“数据库查询”延伸到“文件输出”也呼应了企业系统里常见的“工资表要发给财务”的真实需求。如果时间紧张只做导出不做导入答辩时再提一句“导入可以用同样的思路解析 Excel 后批量入库”想法到位就够了。第二个是加操作日志表。这是很多人想不到但老师很看重的点。建一张login_log或者op_log表记录“谁在什么时间做了什么操作”登录成功后插入一条敏感操作发放工资、删除员工也插入一条。它本身很简单但体现了审计意识答辩时可以说“系统需要追溯操作记录所以设计了日志表”。第三个是演示前做一次全流程自检也是我认为最重要的一个习惯。我当年答辩吃过亏功能全是好的但演示机上的 MySQL 服务没启动启动后密码不对最后只能用截图硬撑。自检清单其实就三行MySQL 服务是否运行db.properties 的账号密码是否对测试数据是否完整。这三件事花五分钟就能确认却决定了你能不能顺利开场。老师不一定在乎功能多炫但一定在乎你的系统是不是一个“随时能跑”的状态。课设是很奇妙的一件事代码量不一定要大但每一个设计点都要能解释清楚。真正拉开差距的往往不是技术复杂度而是你有没有认真对待那些“显而易见”的细节字段类型选对了吗资源关了吗事务加了吗外键在保护什么。希望这些经验能帮你少踩几个坑把这次课程设计做成简历里拿得出手的一笔。希望帮到你。本文还有配套的精品资源点击获取
返回列表