文档:增加每日任务文档

This commit is contained in:
seahi 2024-12-23 20:28:32 +08:00
parent c03ded4930
commit 4c8a1da4f8
5 changed files with 1164 additions and 0 deletions

189
doc/day1.md Normal file
View File

@ -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');
```

307
doc/day2.md Normal file
View File

@ -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. 定期运行所有测试,确保修改不会影响其他功能

451
doc/day3.md Normal file
View File

@ -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 <T> 实体类型
*/
public interface BaseDAO<T> {
/**
* 插入一条记录
* @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<T> findAll();
}
```
2. **StudentDAO接口**
StudentDAO 接口应该继承 BaseDAO 接口,同时扩展学生相关的数据库操作。
```java
package dao;
import model.Student;
import java.util.List;
/**
* 学生DAO接口
*/
public interface StudentDAO extends BaseDAO<Student> {
/**
* 根据学号查询学生
* @param studentId 学号
* @return 学生对象
*/
Student findByStudentId(String studentId);
/**
* 根据姓名模糊查询学生
* @param name 学生姓名
* @return 学生列表
*/
List<Student> findByNameLike(String name);
}
```
3. **TeacherDAO接口**
教师的 DAO 接口应该继承 BaseDAO 接口,同时扩展教师相关的数据库操作。
```java
package dao;
import model.Teacher;
import java.util.List;
/**
* 教师DAO接口
*/
public interface TeacherDAO extends BaseDAO<Teacher> {
/**
* 根据工号查询教师
* @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<LeaveRequest> {
/**
* 根据学生ID查询请假记录
* @param studentId 学生ID
* @return 请假记录列表
*/
List<LeaveRequest> findByStudentId(int studentId);
/**
* 根据审批状态查询请假记录
* @param status 审批状态
* @return 请假记录列表
*/
List<LeaveRequest> 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<Student> findAll() {
List<Student> 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<Student> findByNameLike(String name) {
List<Student> 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 >}}

217
doc/day4.md Normal file
View File

@ -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<Teacher> findAll() {
List<Teacher> 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
```

0
doc/day5.md Normal file
View File