From 4c8a1da4f88705f470b6c7cf272d19851d60a71a Mon Sep 17 00:00:00 2001 From: seahi Date: Mon, 23 Dec 2024 20:28:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=AF=8F=E6=97=A5=E4=BB=BB=E5=8A=A1=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/day1.md | 189 ++++++++++++++++++++++ doc/day2.md | 307 +++++++++++++++++++++++++++++++++++ doc/day3.md | 451 ++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/day4.md | 217 +++++++++++++++++++++++++ doc/day5.md | 0 5 files changed, 1164 insertions(+) create mode 100644 doc/day1.md create mode 100644 doc/day2.md create mode 100644 doc/day3.md create mode 100644 doc/day4.md create mode 100644 doc/day5.md diff --git a/doc/day1.md b/doc/day1.md new file mode 100644 index 0000000..cb19430 --- /dev/null +++ b/doc/day1.md @@ -0,0 +1,189 @@ + + +基于Java Swing的学生请假管理系统的设计和实现文档。文档内容包括项目结构、技术栈、功能特性、运行环境要求以及**第一天的项目框架搭建任务步骤**。 + +## 项目结构 + +``` +AbsenceManager/ +├── src/ # 源代码目录 +│ ├── dao/ # 数据访问层 +│ │ └── impl/ # DAO接口实现类 +│ ├── service/ # 业务逻辑层 +│ ├── model/ # 数据模型层 +│ ├── gui/ # 图形界面层 +│ └── util/ # 工具类 +├── lib/ # 第三方依赖库 +├── sql/ # 数据库相关文件 +│ └── init.sql # 数据库初始化脚本 +└── README.md # 项目说明文档 +``` + + +## 技术栈 + +- Java SE +- Java Swing (GUI界面) +- MySQL (数据库) +- JDBC (数据库连接) +## 功能特性 + +1. 用户管理 + - 学生账号登录 + - 教师账号登录 +2. 请假申请 + - 学生提交日常出校申请 + - 支持多种请假类型(学习、就医、办事等) + - 自动计算请假时长 + - 详细事由说明 +3. 请假审批 + - 教师查看待审批申请 + - 按状态筛选(待审批、已通过、已拒绝) + - 教师审批处理 + - 审批意见填写 +4. 信息查询 + - 学生查看个人请假记录 + - 教师查看所有请假记录 + - 按时间范围查询 + - 按审批状态筛选 + - 双击查看详细信息 +5. 界面特性 + - 简洁美观的Swing界面 + - 表格化展示信息 + - 详情对话框展示 + +## 运行环境要求 + +- JDK 8 或更高版本 +- MySQL 5.7 或更高版本 +- IDE推荐使用IntelliJ IDEA +## Day 1 - 项目框架搭建 + +### 任务步骤 + +1. 项目初始化 + - 创建Java项目 + - 建立项目目录结构(src, lib, sql等,参考“项目结构”部分) + - 导入必要的第三方库(mysql-connector-java.jar) + +2. 数据库配置 + - 创建MySQL数据库(absence_manager) + - 运行sql/init.sql脚本创建表结构 + - 配置数据库连接(src/util/DatabaseUtil.java) + +3. 实现学生实体类(model/Student.java) + +```java +public class Student { + private int id; // 数据库主键ID + private String studentId; // 学号 + private String name; // 学生姓名 + private String className; // 班级 + private String contact; // 联系方式 + private String college; // 学院 + private String major; // 专业 + private String password; // 登录密码 + + // 构造方法 + // getter和setter方法 +} +``` + +4. 测试运行 + - 测试数据库连接 + - 确保项目能够正常编译运行 + +### 完成标准 + +- [ ] 项目结构完整,包含所有必要的目录 +- [ ] 成功导入所需的第三方库 +- [ ] 数据库表结构创建完成 +- [ ] 数据库连接配置正确 +- [ ] Student类实现完整,包含所有必要的属性和方法 +- [ ] 项目可以正常编译运行 + +### 注意事项 + +1. 建立项目时注意包结构的规范性 +2. 数据库字符集使用utf8mb4,避免中文乱码 +3. 代码编写要规范,加上必要的注释 +4. 提交代码前进行本地测试,确保无编译错误 + +### 参考代码 + +#### util/DatabaseUtil.java + +![](https://static.seahi.me/2024/12/202412171225346.png) + +#### sql/init.sql + +```sql +-- 创建数据库 +CREATE DATABASE IF NOT EXISTS absence_manager DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +USE absence_manager; + +-- 创建学生表 +CREATE TABLE IF NOT EXISTS students ( + id INT PRIMARY KEY AUTO_INCREMENT, + student_id VARCHAR(20) UNIQUE NOT NULL, -- 学号 + name VARCHAR(50) NOT NULL, -- 姓名 + class_name VARCHAR(50) NOT NULL, -- 班级 + contact VARCHAR(20), -- 联系方式 + college VARCHAR(50) NOT NULL, -- 学院 + major VARCHAR(50) NOT NULL, -- 专业 + password VARCHAR(50) NOT NULL -- 密码 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表'; + +-- 创建教师表 +CREATE TABLE IF NOT EXISTS teachers ( + id INT PRIMARY KEY AUTO_INCREMENT, + teacher_id VARCHAR(20) UNIQUE NOT NULL, -- 工号 + name VARCHAR(50) NOT NULL, -- 姓名 + department VARCHAR(50) NOT NULL, -- 部门 + contact VARCHAR(20), -- 联系方式 + password VARCHAR(50) NOT NULL -- 密码 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='教师信息表'; + +-- 创建请假申请表 +CREATE TABLE IF NOT EXISTS leave_requests ( + id INT PRIMARY KEY AUTO_INCREMENT, + student_id INT NOT NULL, -- 关联学生ID + start_time DATETIME NOT NULL, -- 开始时间 + end_time DATETIME NOT NULL, -- 结束时间 + duration DECIMAL(5,2) NOT NULL, -- 时长 + location VARCHAR(100) NOT NULL, -- 外出地址 + reason_type VARCHAR(50) NOT NULL, -- 外出事由类型 + reason_detail TEXT, -- 详细事由 + is_leaving_city BOOLEAN DEFAULT FALSE, -- 是否离津 + status VARCHAR(20) NOT NULL DEFAULT 'PENDING', -- 审批状态 + approver_id INT, -- 审批人ID + approval_comment TEXT, -- 审批意见 + request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 申请时间 + approval_time TIMESTAMP NULL, -- 审批时间 + FOREIGN KEY (student_id) REFERENCES students(id), + FOREIGN KEY (approver_id) REFERENCES teachers(id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='请假申请表'; + +-- 插入示例数据 +INSERT INTO students (student_id, name, class_name, contact, college, major, password) VALUES + ('2023208145', '李同学', '云计算G23-1', '15842087237', '信息工程学院', '云计算技术应用', '123456'), + ('2023208146', '张同学', '云计算G23-1', '15842087238', '信息工程学院', '云计算技术应用', '123456'); + +INSERT INTO teachers (teacher_id, name, department, contact, password) VALUES + ('2023001', '王老师', '信息工程学院', '13912345678', '123456'), + ('2023002', '李老师', '信息工程学院', '13912345679', '123456'); + +-- 插入请假申请示例数据 +INSERT INTO leave_requests (student_id, start_time, end_time, duration, location, reason_type, reason_detail, is_leaving_city, status, approver_id, approval_comment, request_time) VALUES + (1, '2024-01-15 08:00:00', '2024-01-15 17:00:00', 9.00, '天津市河西区图书馆', '学习', '去图书馆学习准备考试', false, 'APPROVED', 1, '同意', '2024-01-14 10:00:00'), + (2, '2024-01-16 13:00:00', '2024-01-16 17:30:00', 4.50, '天津市南开医院', '就医', '牙科复诊', false, 'APPROVED', 1, '注意安全', '2024-01-15 09:30:00'), + (1, '2024-01-17 09:00:00', '2024-01-17 12:00:00', 3.00, '天津市档案馆', '办事', '办理个人档案', false, 'PENDING', null, null, '2024-01-16 16:20:00'), + (2, '2024-01-18 14:00:00', '2024-01-18 18:00:00', 4.00, '和平区文化中心', '活动', '参加志愿者活动', false, 'REJECTED', 2, '活动时间与课程冲突', '2024-01-17 11:15:00'), + (1, '2024-01-19 10:00:00', '2024-01-19 16:00:00', 6.00, '天津站', '返乡', '购买返乡车票', true, 'APPROVED', 2, '请注意防疫', '2024-01-18 14:40:00'), + (2, '2024-01-20 09:30:00', '2024-01-20 11:30:00', 2.00, '河西区人才市场', '求职', '参加招聘会', false, 'PENDING', null, null, '2024-01-19 17:00:00'), + (1, '2024-01-21 13:00:00', '2024-01-21 17:00:00', 4.00, '天津市第一中心医院', '就医', '例行体检', false, 'APPROVED', 1, '准假', '2024-01-20 10:25:00'), + (2, '2024-01-22 08:00:00', '2024-01-22 18:00:00', 10.00, '滨海新区图书馆', '实习', '企业实地考察', false, 'PENDING', null, null, '2024-01-21 15:30:00'), + (1, '2024-01-23 14:00:00', '2024-01-23 16:00:00', 2.00, '南开区政务中心', '办事', '办理身份证', false, 'APPROVED', 2, '同意', '2024-01-22 09:45:00'), + (2, '2024-01-24 09:00:00', '2024-01-24 18:00:00', 9.00, '天津市人民医院', '就医', '陪同父亲做检查', false, 'PENDING', null, null, '2024-01-23 16:50:00'); +``` diff --git a/doc/day2.md b/doc/day2.md new file mode 100644 index 0000000..06b9120 --- /dev/null +++ b/doc/day2.md @@ -0,0 +1,307 @@ +本次实验的主要任务是完成期末案例的实体类开发和单元测试,并且熟悉IDEA工具的代码自动生成功能。 + +## 环境准备 + + +{{< admonition warning >}} +每天的代码要保留一份单独的压缩包,命名示例:`day1.rar` +{{< /admonition >}} + +1. 导入JUnit依赖(下方两个包放到lib目录) +2. 配置好MySQL数据库(参考Day 1) + +[junit-4.13.2.jar](https://static.seahi.me/2024/12/junit-4.13.2.jar) +[hamcrest-core-1.3.jar](https://static.seahi.me/2024/12/hamcrest-core-1.3.jar) + +{{< admonition quote 包说明>}} +1. junit-4.13.2.jar 是一个用来测试Java程序的工具 +2. hamcrest-core-1.3.jar 是JUnit工具的依赖包 + {{< /admonition >}} + +## 任务内容 + +### 1. 实体类开发 + +#### 1.1 请假申请实体类(LeaveRequest.java) + +```java +package model; + +import java.util.Date; + +/** + * 请假条实体类 + */ +public class LeaveRequest { + private int id; // 数据库主键ID + private Student student; // 学生信息 + private Date startTime; // 开始时间 + private Date endTime; // 结束时间 + private double duration; // 时长 + private String location; // 外出地址 + private String reasonType; // 外出事由类型 + private String reasonDetail; // 详细事由 + private boolean isLeavingCity; // 是否离津 + private ApprovalStatus status; // 审批状态 + private Teacher approver; // 审批人 + private String approvalComment; // 审批意见 + private Date requestTime; // 申请时间 + private Date approvalTime; // 审批时间 + + // 构造方法 + // getter和setter方法 +} +``` + +**操作步骤:** +1. 在`src/model`目录下创建`LeaveRequest.java`文件 +2. 复制上述代码框架 +3. 实现默认构造方法,设置初始状态为`PENDING` +4. 使用IDE**自动生成**所有属性的`getter`和`setter`方法 + +#### 1.2 审批状态枚举(ApprovalStatus.java) + +> 该文件的代码是完整的,无需修改 + +```java +package model; + +/** + * 请假审批状态枚举类 + */ +public enum ApprovalStatus { + PENDING("待审批"), // 等待审批 + APPROVED("已批准"), // 已经批准 + REJECTED("已驳回"); // 已经驳回 + + private final String description; // 状态描述 + + ApprovalStatus(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} +``` + +> enum是枚举类型,用于表示一组固定的常量。 + + + +{{< admonition >}} +类似地步骤,完成`Teacher`实体类和`Student`实体类 +{{< /admonition >}} + +#### 1.3 教师实体类(Teacher.java) + +```java +package model; + +/** + * 教师实体类 + */ +public class Teacher { + private int id; // 数据库主键ID + private String teacherId; // 教师工号 + private String name; // 教师姓名 + private String department; // 所属部门 + private String contact; // 联系方式 + private String password; // 登录密码 + + // 使用 IDE 自动生成以下内容: + // 构造方法 + // getter 和 setter 方法 +} +``` +### 2. 单元测试开发 + +**什么是单元测试?** + +单元测试是检查程序中最小单位(一个方法、一个功能)是否正确运行的测试。 + +**为什么要做单元测试?** +- 及早发现程序中的错误 +- 确保修改代码后,原有功能仍然正常 +- 节省手工测试的时间 + +**单元测试的优点** +- 自动化:写好测试代码后可以反复运行 +- 快速:一次能测试多个功能点 +- 可靠:比人工测试更准确 + +在实际开发中,单元测试是保证代码质量的重要手段。以后你们进入公司工作,很可能会遇到需要写单元测试的要求。对Java代码进行单元测试,常用的工具(包)是Junit + +{{< admonition success 注意>}} +本次任务需要了解单元测试的意义、方法,后期的单元测试代码会给同学们提供完整代码。 +{{< /admonition >}} + +#### 2.1 创建测试目录 + +1. 在`src`目录下创建`test`目录 +2. 在`test`目录下创建`model`子目录 +3. 在`test/model`目录下创建各个测试类 + +#### 2.2 编写Teacher测试类(TeacherTest.java) + +```java +package test.model; + +import model.Teacher; +import org.junit.Test; +import static org.junit.Assert.*; + +public class TeacherTest { + + @Test + public void testConstructorAndGetters() { + String teacherId = "T001"; + String name = "张老师"; + String department = "计算机系"; + String contact = "13800138000"; + String password = "password123"; + + Teacher teacher = new Teacher(teacherId, name, department, contact, password); + + assertEquals("教师工号应匹配", teacherId, teacher.getTeacherId()); + assertEquals("教师姓名应匹配", name, teacher.getName()); + assertEquals("所属部门应匹配", department, teacher.getDepartment()); + assertEquals("联系方式应匹配", contact, teacher.getContact()); + assertEquals("密码应匹配", password, teacher.getPassword()); + } + + @Test + public void testSetters() { + Teacher teacher = new Teacher(); + + int id = 1; + String teacherId = "T002"; + String name = "李老师"; + String department = "数学系"; + String contact = "13900139000"; + String password = "newpass123"; + + teacher.setId(id); + teacher.setTeacherId(teacherId); + teacher.setName(name); + teacher.setDepartment(department); + teacher.setContact(contact); + teacher.setPassword(password); + + assertEquals("ID应匹配", id, teacher.getId()); + assertEquals("教师工号应匹配", teacherId, teacher.getTeacherId()); + assertEquals("教师姓名应匹配", name, teacher.getName()); + assertEquals("所属部门应匹配", department, teacher.getDepartment()); + assertEquals("联系方式应匹配", contact, teacher.getContact()); + assertEquals("密码应匹配", password, teacher.getPassword()); + } +} +``` + +**操作步骤:** +1. 创建TeacherTest.java文件 +2. 编写构造方法测试用例 +3. 编写getter/setter方法测试用例 + +#### 2.4 编写DatabaseUtil测试类(DatabaseUtilTest.java) + +```java +package test.util; + +import org.junit.Test; +import util.DatabaseUtil; + +import java.sql.Connection; +import java.sql.SQLException; + +import static org.junit.Assert.*; + +public class DatabaseUtilTest { + + @Test + public void testGetConnection() { + try (Connection conn = DatabaseUtil.getConnection()) { + // 验证连接不为空 + assertNotNull("数据库连接不应该为空", conn); + // 验证连接是否有效 + assertTrue("数据库连接应该是有效的", !conn.isClosed()); + } catch (SQLException e) { + fail("获取数据库连接时发生异常: " + e.getMessage()); + } + } + + @Test + public void testConnectionAutoClose() { + Connection conn = null; + try { + // 在try-with-resources块中获取连接 + try (Connection autoCloseConn = DatabaseUtil.getConnection()) { + assertNotNull("数据库连接不应该为空", autoCloseConn); + assertTrue("连接应该是开启状态", !autoCloseConn.isClosed()); + } + // try-with-resources块结束后,获取一个新连接来测试 + conn = DatabaseUtil.getConnection(); + assertNotNull("新的数据库连接不应该为空", conn); + assertTrue("新的连接应该是开启状态", !conn.isClosed()); + } catch (SQLException e) { + fail("测试连接自动关闭时发生异常: " + e.getMessage()); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + } +} +``` + +**操作步骤:** +1. 在test.util包下创建DatabaseUtilTest.java文件 +2. 编写测试数据库连接的方法testGetConnection() +3. 编写测试连接自动关闭的方法testConnectionAutoClose() +4. 确保使用try-with-resources语句来自动管理数据库连接资源 + +## 完成标准 + +- [ ] 所有实体类完整实现 + - [ ] Teacher类(属性、构造方法、getter/setter) + - [ ] Student类(属性、构造方法、getter/setter) + - [ ] LeaveRequest类(属性、构造方法、getter/setter) + - [ ] ApprovalStatus枚举(所有状态值和描述) +- [ ] 所有单元测试通过 + - [ ] TeacherTest全部测试用例通过 + - [ ] DatabaseUtilTest全部测试用例通过 +- [ ] 代码规范 + - [ ] 适当的注释说明 + - [ ] 规范的命名 + - [ ] 合理的代码组织 + +## 编码规范 + +1. 类名使用大驼峰命名法(如:`TeacherTest`) +2. 方法名使用小驼峰命名法(如:`testConstructor`) +3. 变量名使用小驼峰命名法(如:`teacherId`) +4. 常量使用全大写(如:`PENDING`) +5. 每个类都要有类注释 +6. 重要方法要有方法注释 + +## 测试运行方法 + +1. 在IDE中右键点击test包 +2. 选择"Run 'Tests in test'" 运行所有测试类 +3. 或右键点击test内的某个包,选择"Run 'Tests in 包名'"运行包内所有测试 +4. 或右键点击单个测试类,选择"Run 'Tests in 类名'"运行单个测试类 +5. 查看测试结果:绿色√表示通过,红色×表示失败 + + +## 提示与建议 + +1. 使用IDE的代码生成功能可以快速生成getter/setter方法 +2. 测试用例要考虑各种可能的情况 +3. 测试失败时要仔细阅读错误信息 +4. 定期运行所有测试,确保修改不会影响其他功能 + diff --git a/doc/day3.md b/doc/day3.md new file mode 100644 index 0000000..0c4a44d --- /dev/null +++ b/doc/day3.md @@ -0,0 +1,451 @@ +本次实验的主要任务是完成期末案例的**数据访问层**(Data Access Object, DAO)开发和其单元测试,并且再次熟悉单元测试的使用方法。 + + + +## 准备工作 + +{{< admonition info 环境准备>}} +在开始今天的任务之前,请确保你已经完成了前两天的任务,包括项目框架搭建和实体类的开发等。 +{{< /admonition >}} + +### 注册账号并建立代码仓库 + +[Gitea](https://git.seahi.me) + +注册一个账号,成功后,点击占上角的+创建仓库: + +![](https://static.seahi.me/2024/12/202412191440855.png) + +按下列信息填写: + +- 仓库名:stu00,把00换成自己学号的后两位 +- 仓库描述:填写姓名 +- 可见性:私有 + +![](https://static.seahi.me/2024/12/202412200844243.png) + + +### 安装Git工具 + +[下载](https://static.seahi.me/2024/12/Git-2.47.1-64-bit.exe) + +### 解压项目 + +将上节课保存好的项目解压到桌面上,最终效果如下: + +![](https://static.seahi.me/2024/12/202412191438488.png) + +在代码目录点击右键,选择 Open Git Bash here(意思是“在这里打开Git Bash”) + +![](https://static.seahi.me/2024/12/202412191439007.png) + +### 提交项目 + +**在刚刚打开的 Git Bash 中**执行下列命令: + +```bash +git init +git checkout -b main +git add . +git commit -m "完成Day2" +git remote add origin https://git.seahi.me/用户名/stu00.git # 00换成学号 +git push -u origin main +``` + +执行完成后,刷新 Gitea 网页,可以看到自己的代码 + +## 任务一:DAO层接口开发 + +在本任务中,我们将开发数据访问层(DAO)的接口。**DAO层是连接业务逻辑和数据库的桥梁,通过定义统一的接口规范,我们可以确保数据访问的一致性和可维护性。** + +### 需要开发的接口 + +{{< admonition info 提示>}} +通过代码中的包名来判断该文件应该处于哪个目录。 +{{< /admonition >}} + +1. **BaseDAO接口** + +基础DAO接口,该接口规定通用的CRUD操作应该包含哪些操作。其他具体的DAO接口应该继承自该接口。 + +{{< admonition quote >}} +CRUD的含义是: +- **C** 创建 Create +- **R** 读取 Read +- **U** 更新 Update +- **D** 删除 Delete + CRUD即平时说的增、删、改、查。 + {{< /admonition >}} + +```java +package dao; + +import java.util.List; + +/** + * 基础DAO接口,定义通用的CRUD操作 + * @param 实体类型 + */ +public interface BaseDAO { + /** + * 插入一条记录 + * @param entity 实体对象 + * @return 影响的行数 + */ + int insert(T entity); + + /** + * 根据ID删除记录 + * @param id 主键ID + * @return 影响的行数 + */ + int deleteById(int id); + + /** + * 更新记录 + * @param entity 实体对象 + * @return 影响的行数 + */ + int update(T entity); + + /** + * 根据ID查询记录 + * @param id 主键ID + * @return 实体对象 + */ + T findById(int id); + + /** + * 查询所有记录 + * @return 实体对象列表 + */ + List findAll(); +} + +``` + +2. **StudentDAO接口** + +StudentDAO 接口应该继承 BaseDAO 接口,同时扩展学生相关的数据库操作。 + +```java +package dao; + +import model.Student; +import java.util.List; + +/** + * 学生DAO接口 + */ +public interface StudentDAO extends BaseDAO { + /** + * 根据学号查询学生 + * @param studentId 学号 + * @return 学生对象 + */ + Student findByStudentId(String studentId); + + /** + * 根据姓名模糊查询学生 + * @param name 学生姓名 + * @return 学生列表 + */ + List findByNameLike(String name); +} +``` + +3. **TeacherDAO接口** + +教师的 DAO 接口应该继承 BaseDAO 接口,同时扩展教师相关的数据库操作。 + +```java +package dao; + +import model.Teacher; +import java.util.List; + +/** + * 教师DAO接口 + */ +public interface TeacherDAO extends BaseDAO { + /** + * 根据工号查询教师 + * @param teacherId 教师工号 + * @return 教师对象 + */ + Teacher findByTeacherId(String teacherId); +} +``` + +4. **LeaveRequestDAO接口** + +```java +package dao; + +import model.LeaveRequest; +import model.ApprovalStatus; +import java.util.Date; +import java.util.List; + +/** + * 请假申请DAO接口 + */ +public interface LeaveRequestDAO extends BaseDAO { + /** + * 根据学生ID查询请假记录 + * @param studentId 学生ID + * @return 请假记录列表 + */ + List findByStudentId(int studentId); + + /** + * 根据审批状态查询请假记录 + * @param status 审批状态 + * @return 请假记录列表 + */ + List findByStatus(ApprovalStatus status); + + + /** + * 更新请假申请状态 + * @param id 请假申请ID + * @param status 新状态 + * @param approverComment 审批意见 + * @return 影响的行数 + */ + int updateStatus(int id, ApprovalStatus status, String approverComment); +} +``` + + + +## 任务二:StudentDAOImpl类开发 + + +在完成了DAO层接口的定义后,我们需要**实现这些接口**。本任务将以`StudentDAOImpl`为例,详细讲解如何实现一个完整的DAO实现类。`StudentDAOImpl`类需要实现`StudentDAO`接口,提供对学生数据的具体数据库操作实现。 + + +### 1. 创建类基本结构 + +```java +package dao.impl; + +/** + * StudentDAO接口的实现类,提供对学生数据的访问操作 + * 实现了StudentDAO接口定义的所有方法,包括基础的CRUD操作和特定的查询方法 + * 使用JDBC与数据库进行交互,所有数据库操作都包含适当的异常处理 + */ +public class StudentDAOImpl implements StudentDAO { + // TODO 实现StudentDAO接口的所有方法 +} +``` + +### 2. 实现insert方法 + +```java +/** + * 插入一条学生记录 + * 将Student对象的信息保存到数据库中,并获取生成的主键ID + * + * @param student 要插入的学生对象,包含学生的详细信息 + * @return 影响的行数,插入成功返回1,失败返回0 + */ +@Override +public int insert(Student student) { + String sql = "INSERT INTO students (student_id, name, class_name, contact, college, major, password) VALUES (?, ?, ?, ?, ?, ?, ?)"; + try (Connection conn = DatabaseUtil.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { + + // 设置预处理语句的参数 + stmt.setString(1, student.getStudentId()); + stmt.setString(2, student.getName()); + stmt.setString(3, student.getClassName()); + stmt.setString(4, student.getContact()); + stmt.setString(5, student.getCollege()); + stmt.setString(6, student.getMajor()); + stmt.setString(7, student.getPassword()); + + int affectedRows = stmt.executeUpdate(); + if (affectedRows == 0) { + return 0; + } + + // 获取自动生成的主键 + try (ResultSet generatedKeys = stmt.getGeneratedKeys()) { + if (generatedKeys.next()) { + student.setId(generatedKeys.getInt(1)); + } + } + return affectedRows; + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } +} +``` + +{{< admonition info 关于注解>}} +@Override是一个注解,作用是**表示当前方法重写了父类的方法**。 +它的主要作用是: +1. 防止写错方法名:如果方法名写错了,编译器会报错提醒你 +2. 提高代码可读性:让其他程序员一眼就能看出这是一个重写的方法 + {{< /admonition >}} + + +### 3. 实现查询方法 + +```java +/** + * 根据ID查询学生信息 + * + * @param id 要查询的学生ID + * @return 如果找到则返回学生对象,否则返回null + */ +@Override +public Student findById(int id) { + String sql = "SELECT * FROM students WHERE id = ?"; + try (Connection conn = DatabaseUtil.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setInt(1, id); + ResultSet rs = stmt.executeQuery(); + + if (rs.next()) { + return mapResultSetToStudent(rs); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; +} +``` + +仿照以上示例,完成下列两个方法: + +```java +/** + * 查询所有学生记录 + * + * @return 包含所有学生对象的列表,如果没有记录则返回空列表 + */ +@Override +public List findAll() { + List students = new ArrayList<>(); + String sql = "SELECT * FROM students"; + try (Connection conn = DatabaseUtil.getConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + + while (rs.next()) { + students.add(mapResultSetToStudent(rs)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return students; +} + +/** + * 根据姓名模糊查询学生信息 + * + * @param name 要查询的学生姓名(支持模糊查询) + * @return 符合条件的学生对象列表,如果没有匹配则返回空列表 + */ +@Override +public List findByNameLike(String name) { + List students = new ArrayList<>(); + String sql = "SELECT * FROM students WHERE name LIKE ?"; + try (Connection conn = DatabaseUtil.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, "%" + name + "%"); + ResultSet rs = stmt.executeQuery(); + + while (rs.next()) { + students.add(mapResultSetToStudent(rs)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return students; +} + +/** + * 根据学号查询学生信息 + * + * @param studentId 要查询的学号 + * @return 如果找到则返回学生对象,否则返回null + */ +@Override +public Student findByStudentId(String studentId) { + // TODO 根据数据库中表的定义,完成该方法 +} +``` + +### 4. 实现ResultSet映射方法 + +```java +/** + * 将ResultSet映射为Student对象 + * 从结果集中提取数据并创建Student实例 + * + * @param rs 包含学生数据的ResultSet对象 + * @return 映射后的Student对象 + * @throws SQLException 如果访问ResultSet时发生错误 + */ +private Student mapResultSetToStudent(ResultSet rs) throws SQLException { + Student student = new Student(); + student.setId(rs.getInt("id")); + student.setStudentId(rs.getString("student_id")); + student.setName(rs.getString("name")); + student.setClassName(rs.getString("class_name")); + student.setContact(rs.getString("contact")); + student.setCollege(rs.getString("college")); + student.setMajor(rs.getString("major")); + student.setPassword(rs.getString("password")); + return student; +} +``` + +## 任务三:单元测试 + +将下列文件放置在 `src/test/dao/impl` 目录中,检查测试是否通过 + +[StudentDAOImplTest.java](https://static.seahi.me/2024/12/StudentDAOImplTest.java) + + +## 附 + +### 如何获取课上代码 + +1. 确保电脑已安装Git +2. 选择一个文件夹存放代码 +3. 在该文件夹内部右键,选择"open Git Bash Here" +4. 输入命令: + +```bash +# 第一次获取 +git clone https://git.seahi.me/用户名/stu学号.git +# 后期再次获取时,直接进入到项目目录,执行下列命令即可 +git pull +``` + +> 注意:把"学号"替换成你的实际学号后两位 + +### 如何提交修改的代码 + +当你在自己电脑上修改了代码后,需要提交到仓库: + +1. 打开Git Bash +2. 输入以下命令: + +``` +git add . +git commit -m "修改说明" +git push +``` + +{{< admonition tip "提示">}} +- 第一次使用时需要设置用户名和密码 +- 建议经常提交代码,避免代码丢失 + {{< /admonition >}} \ No newline at end of file diff --git a/doc/day4.md b/doc/day4.md new file mode 100644 index 0000000..a4c1417 --- /dev/null +++ b/doc/day4.md @@ -0,0 +1,217 @@ +本次任务是完成DAO接口实现类开发,并进行单元测试。 + +## 准备工作 + +### 安装Git工具 + +[下载](https://static.seahi.me/2024/12/Git-2.47.1-64-bit.exe) + +### 同步项目代码 + +在桌面上右键,选择 Open Git Bash here + +执行命令: + +```bash +git clone https://git.seahi.me/用户名/stu学号.git +``` + + +## 任务一:完成DAO接口的实现类 + +Day 3 任务中已经完成了`StudentdAOImpl`类的实现,接下来继续完成 `TeacherDaoImpl`和`LeaveRequestDAOImpl`类,这两个类分别实现`TeacherDAO`接口和`LeaveRequestDAO`接口。 + +#### StudentDAOImpl + +[StudentDAOImpl.java](https://static.seahi.me/2024/12/StudentDAOImpl.java) + +### TeacherDAOImpl + +```java +package dao.impl; + +import dao.TeacherDAO; +import model.Teacher; +import util.DatabaseUtil; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +/** + * TeacherDAO接口的实现类,提供教师数据的数据库访问操作 + * 实现了TeacherDAO接口定义的所有方法,包括基础的CRUD操作和特定的查询方法 + * 使用JDBC与数据库进行交互,所有数据库操作都包含适当的异常处理 + */ +public class TeacherDAOImpl implements TeacherDAO { + + /** + * 插入一条教师记录 + * 将Teacher对象的信息保存到数据库中,并获取生成的主键ID + * + * @param teacher 要插入的教师对象,包含教师的详细信息 + * @return 影响的行数,插入成功返回1,失败返回0 + */ + @Override + public int insert(Teacher teacher) { + String sql = "INSERT INTO teachers (teacher_id, name, department, contact, password) VALUES (?, ?, ?, ?, ?)"; + try (Connection conn = DatabaseUtil.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { + + // 设置预处理语句的参数 + stmt.setString(1, teacher.getTeacherId()); + stmt.setString(2, teacher.getName()); + stmt.setString(3, teacher.getDepartment()); + stmt.setString(4, teacher.getContact()); + stmt.setString(5, teacher.getPassword()); + + int affectedRows = stmt.executeUpdate(); + if (affectedRows == 0) { + return 0; + } + + // 获取自动生成的主键 + try (ResultSet generatedKeys = stmt.getGeneratedKeys()) { + if (generatedKeys.next()) { + teacher.setId(generatedKeys.getInt(1)); + } + } + return affectedRows; + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 根据ID删除教师记录 + * + * @param id 要删除的教师记录的ID + * @return 影响的行数,删除成功返回1,失败返回0 + */ + @Override + public int deleteById(int id) { + String sql = "DELETE FROM teachers WHERE id = ?"; + try (Connection conn = DatabaseUtil.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setInt(1, id); + return stmt.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 根据ID查询教师信息 + * + * @param id 要查询的教师ID + * @return 如果找到则返回教师对象,否则返回null + */ + @Override + public Teacher findById(int id) { + String sql = "SELECT * FROM teachers WHERE id = ?"; + try (Connection conn = DatabaseUtil.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setInt(1, id); + ResultSet rs = stmt.executeQuery(); + + if (rs.next()) { + return mapResultSetToTeacher(rs); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 查询所有教师记录 + * + * @return 包含所有教师对象的列表,如果没有记录则返回空列表 + */ + @Override + public List findAll() { + List teachers = new ArrayList<>(); + String sql = "SELECT * FROM teachers"; + try (Connection conn = DatabaseUtil.getConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + + while (rs.next()) { + teachers.add(mapResultSetToTeacher(rs)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return teachers; + } + + /** + * 根据工号查询教师信息 + * + * @param teacherId 要查询的教师工号 + * @return 如果找到则返回教师对象,否则返回null + */ + @Override + public Teacher findByTeacherId(String teacherId) { + String sql = "SELECT * FROM teachers WHERE teacher_id = ?"; + try (Connection conn = DatabaseUtil.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, teacherId); + ResultSet rs = stmt.executeQuery(); + + if (rs.next()) { + return mapResultSetToTeacher(rs); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 将ResultSet映射为Teacher对象 + * 从结果集中提取数据并创建Teacher实例 + * + * @param rs 包含教师数据的ResultSet对象 + * @return 映射后的Teacher对象 + * @throws SQLException 如果访问ResultSet时发生错误 + */ + private Teacher mapResultSetToTeacher(ResultSet rs) throws SQLException { + // TODO 补全代码(参考Day3 任务二.4) + } +} + +``` + +### LeaveRequestDAOImpl + +源文件:[LeaveRequestDAOImpl.java](https://static.seahi.me/2024/12/LeaveRequestDAOImpl.java) + +下载后放置到合适的位置 + + +## 任务二:DAO实现类的单元测试 + +将下列文件放置在 `src/test/dao/impl` 目录中,检查测试是否通过 + +[TeacherDAOImplTest.java](https://static.seahi.me/2024/12/TeacherDAOImplTest.java) + +[LeaveRequestDAOImplTest.java](https://static.seahi.me/2024/12/LeaveRequestDAOImplTest.java) + +## 提交代码 + +在项目目录中,打开 Git Bash,执行: + +```bash +git config --global user.email "换成你的邮箱" +git config --global user.name "换成你的用户名" + +git add . +git commit -m "完成Day4任务" +git push +``` \ No newline at end of file diff --git a/doc/day5.md b/doc/day5.md new file mode 100644 index 0000000..e69de29