11 KiB
本次实验的主要任务是完成期末案例的数据访问层(Data Access Object, DAO)开发和其单元测试,并且再次熟悉单元测试的使用方法。
准备工作
{{< admonition info 环境准备>}} 在开始今天的任务之前,请确保你已经完成了前两天的任务,包括项目框架搭建和实体类的开发等。 {{< /admonition >}}
注册账号并建立代码仓库
注册一个账号,成功后,点击占上角的+创建仓库:
按下列信息填写:
- 仓库名:stu00,把00换成自己学号的后两位
- 仓库描述:填写姓名
- 可见性:私有
安装Git工具
解压项目
将上节课保存好的项目解压到桌面上,最终效果如下:
在代码目录点击右键,选择 Open Git Bash here(意思是“在这里打开Git Bash”)
提交项目
在刚刚打开的 Git 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 >}}
- BaseDAO接口
基础DAO接口,该接口规定通用的CRUD操作应该包含哪些操作。其他具体的DAO接口应该继承自该接口。
{{< admonition quote >}} CRUD的含义是:
- C 创建 Create
- R 读取 Read
- U 更新 Update
- D 删除 Delete CRUD即平时说的增、删、改、查。 {{< /admonition >}}
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();
}
- StudentDAO接口
StudentDAO 接口应该继承 BaseDAO 接口,同时扩展学生相关的数据库操作。
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);
}
- TeacherDAO接口
教师的 DAO 接口应该继承 BaseDAO 接口,同时扩展教师相关的数据库操作。
package dao;
import model.Teacher;
import java.util.List;
/**
* 教师DAO接口
*/
public interface TeacherDAO extends BaseDAO<Teacher> {
/**
* 根据工号查询教师
* @param teacherId 教师工号
* @return 教师对象
*/
Teacher findByTeacherId(String teacherId);
}
- LeaveRequestDAO接口
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. 创建类基本结构
package dao.impl;
/**
* StudentDAO接口的实现类,提供对学生数据的访问操作
* 实现了StudentDAO接口定义的所有方法,包括基础的CRUD操作和特定的查询方法
* 使用JDBC与数据库进行交互,所有数据库操作都包含适当的异常处理
*/
public class StudentDAOImpl implements StudentDAO {
// TODO 实现StudentDAO接口的所有方法
}
2. 实现insert方法
/**
* 插入一条学生记录
* 将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是一个注解,作用是表示当前方法重写了父类的方法。 它的主要作用是:
- 防止写错方法名:如果方法名写错了,编译器会报错提醒你
- 提高代码可读性:让其他程序员一眼就能看出这是一个重写的方法 {{< /admonition >}}
3. 实现查询方法
/**
* 根据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;
}
仿照以上示例,完成下列两个方法:
/**
* 查询所有学生记录
*
* @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映射方法
/**
* 将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
目录中,检查测试是否通过
附
如何获取课上代码
- 确保电脑已安装Git
- 选择一个文件夹存放代码
- 在该文件夹内部右键,选择"open Git Bash Here"
- 输入命令:
# 第一次获取
git clone https://git.seahi.me/用户名/stu学号.git
# 后期再次获取时,直接进入到项目目录,执行下列命令即可
git pull
注意:把"学号"替换成你的实际学号后两位
如何提交修改的代码
当你在自己电脑上修改了代码后,需要提交到仓库:
- 打开Git Bash
- 输入以下命令:
git add .
git commit -m "修改说明"
git push
{{< admonition tip "提示">}}
- 第一次使用时需要设置用户名和密码
- 建议经常提交代码,避免代码丢失 {{< /admonition >}}