Java 课程设计:LWZ - Online Judge学生端(数据库设计部分)
数据库设计#
由于 OJ 系统需要存储的数据很多,因此需要有较为全面的数据库设计。首先需要存储用户基本的用户名和密码信息,以及存储用户的类型是老师还是学生。接着为了支持同学的答题,数据库需要存储题目的信息,包括题干和答案等。值得一提的是,一道编程题会有多个测试点,因此编程题需要额外的一张表存储测试点。需要对同学的答案进行记录,需要有表存储不同题型用户的作答情况,同理也需要有一张表存储用户的成绩。在实际情况下教师一般不会一道题一道题地布置,而是一次性布置多道题,以题目集的形式来存储,所以需要表来存储题目集信息。同学加入班级之后,才需要完成自己班上的任务,老师也只能在自己的班级发布任务,因此需要有两张表分别存储班级信息和老师发布的任务信息。最后还需要一张表存储站内短信,数据库的总体设计如下。
用户部分#
users 表#
题目部分#
choice 表#
judgment 表#
编程题存储#
program 表#
testpoint 表#
subjective#
collections 表#
作答记录部分#
codes 表#
answer 表#
score 表#
班级管理部分#
classes 表#
task 表#
站内短信部分#
emails 表#
数据库读写#
对数据库的读写采用 DAO 模式实现,DAO (DataAccessobjects) 数据存取对象是指位于业务逻辑和持久化数据之间,实现对持久化数据的访问的工作模式,具体可以参考博客:Java DAO 模式。在服务器实现了 3 个 DAO 模式接口,来支持裁判机的正常工作。
CodeRepositoryDAO 接口#
CodeRepository 接口指定了向存储层进行代码数据交互的方法,代码和 UML 类图如下。
/**
* CodeRepositoryDAO 接口指定了向存储层进行代码数据交互的方法
* @author 乌漆 WhiteMoon
* @version 1.0
*/
public interface CodeRepositoryDAO {
/**
* 该方法用于将向存储层提交某位同学的某道题的代码
* @param username 提交用户,String
* @param id 题目编号,Integer
* @param code 代码,String
* @param state 解题状态
* @return boolean:提交成功返回true,失败返回false
* @throws SQLException
*/
public boolean submitCode(String username, Integer id , String code, Integer state) throws SQLException;
/**
* 该方法用于将在存储层查找题目是否提提交过
* @param username 提交用户,String
* @param id 题目编号,Integer
* @param state 解题状态
* @return boolean:已存在返回true,不存在返回false
* @throws SQLException
*/
public boolean selectCode(String username, Integer id) throws SQLException;
/**
* 该方法用于将向存储层更新某位同学的某道题的代码
* @param username 提交用户,String
* @param id 题目编号,Integer
* @param code 代码,String
* @param state 解题状态
* @return boolean:提交成功返回true,失败返回false
* @throws SQLException
*/
public boolean updateCode(String username, Integer id , String code, Integer state) throws SQLException;
/**
* 该方法用于返回数据库中对应题目的所有代码样本
* @param id 题目编号,Integer
* @return List<String>:存储所有代码的List集合
* @throws SQLException
*/
public List<String> selectAllCode(Integer id) throws SQLException;
}
PointRepositoryDAO 接口#
PointRepositoryDAO 接口指定了从数据库获取测试点、题目题号和选择判断题答案数据的方法,代码和 UML 类图如下。
/**
* PointRepositoryDAO 接口指定了从数据库获取测试点数据的方法
* @author 林智凯
* @version 1.0
*/
public interface PointRepositoryDAO {
/**
* 该方法用于将从存储层返回对应题目的测试点
* @param num 题目编号,Integer
* @return LinkedList<Testpoint>:List集合,存储对应题目的所有测试点:
* @throws SQLException
*/
public List<Testpoint> getTestpoint(Integer num) throws SQLException;
/**
* 该方法用于将从存储层返回对应题目集包含的所有选择题题号
* @param num 题目集编号,Integer
* @return List<Integer>:List集合,存储对应题目集包含的所有选择题号
* @throws SQLException
*/
public List<Integer> getChoiceNum(Integer num) throws SQLException;
/**
* 该方法用于将从存储层返回对应题目集包含的所有判断题题号
* @param num 题目集编号,Integer
* @return List<Integer>:List集合,存储对应题目集包含的所有判断题号
* @throws SQLException
*/
public List<Integer> getJudgmentNum(Integer num) throws SQLException;
/**
* 该方法用于将从存储层返回对应题目集包含的所有编程题题号
* @param num 题目集编号,Integer
* @return List<Integer>:List集合,存储对应题目集包含的所有编程题号
* @throws SQLException
*/
public List<Integer> getProgrammtNum(Integer num) throws SQLException;
/**
* 该方法用于将从存储层返回对应选择题的答案
* @param choiceNum List<Integer>题目集包含的所有选择题题号
* @return List<String>:List集合,存储对应题号对应的答案
* @throws SQLException
*/
public List<String> getChoiceAnswer(List<Integer> choiceNum) throws SQLException;
/**
* 该方法用于将从存储层返回对应判断题的答案
* @param judgmentNum List<Integer>题目集包含的所有选择题题号
* @return List<Integer>:List集合,存储对应题号对应的答案
* @throws SQLException
*/
public List<String> getJudgmentAnswer(List<Integer> judgmentNum) throws SQLException;
}
ScoreRepositoryDAO 接口#
ScoreRepositoryDAO 接口指定了访问修改存储层中同学作答情况和更新分数的方法,代码和 UML 类图如下。
/**
* ScoreRepositoryDAO 接口指定了修改存储层中同学分数的方法
* @author 林智凯
* @version 1.0
*/
public interface ScoreRepositoryDAO {
/**
* 该方法根据用户的信息,更新题目集的选择题分数
* @param username String 用户名
* @param collectionId Integer 题目集id
* @param classId String 班级名
* @param grade Integer
* @return boolean true为更新成功,false为更新失败
* @throws SQLException
*/
public boolean updateChoiceScore(String username, Integer collectionId, String classId, Integer grade) throws SQLException;
/**
* 该方法根据用户的信息,更新题目集的判断题题分数
* @param username String 用户名
* @param collectionId Integer 题目集id
* @param classId String 班级名
* @param grade Integer 成绩
* @return boolean true为更新成功,false为更新失败
* @throws SQLException
*/
public boolean updateJudgmentScore(String username, Integer collectionId, String classId, Integer grade) throws SQLException;
/**
* 该方法根据用户的信息,更新题目集的编程题题分数
* @param username String 用户名
* @param collectionId Integer 题目集id
* @param classId String 班级名
* @param grade Integer 成绩
* @return boolean true为更新成功,false为更新失败
* @throws SQLException
*/
public boolean updateProgrammingScore(String username, Integer collectionId, String classId, Integer grade) throws SQLException;
/**
* 该方法实现搜索对应题目集的选择题总分
* @param collectionId Integer 题目集id
* @return Integer 题目集选择题总分
* @throws SQLException
*/
public Integer getChoiceScore(Integer collectionId) throws SQLException;
/**
* 该方法实现搜索对应题目集的判断题总分
* @param collectionId Integer 题目集id
* @return Integer 题目集判断题总分
* @throws SQLException
*/
public Integer getJudgmentScore(Integer collectionId) throws SQLException;
/**
* 该方法获取对应用户完成的编程题号
* @param questionId List<Integer> 题目id
* @param username String 用户名
* @return Integer 题目集判断题总分
* @throws SQLException
*/
public List<Integer> getProgramCompleteNum(List<Integer> questionId, String username) throws SQLException;
/**
* 该方法获取用户答的题目的得分
* @param completeNum List<Integer> 已完成题目id
* @return Integer 用户答的题目的总分
* @throws SQLException
*/
public Integer getProgramScore(List<Integer> completeNum) throws SQLException;
}
自建数据库连接池#
按照传统的模式,每次执行 SQL 语句访问数据库时,都需要先建立一条连接,等 SQL 语句执行完毕后切断连接。但是连接的建立需要一定的资源开销,如果需要频繁地对数据库执行 SQL 语句,则这种模式会在连接的建立上造成巨大的开销,导致效率受到影响。此处如果有一个数据库连接池能预先建立多条连接,当需要建立连接时就分出一条连接支持操作,然后再会收回连接池继续利用,就可以优化效率。
此处我根据数据库连接池的原理设计了简易的数据库连接池,使用的是类队列的结构实现的,并且模仿了 C++ vector 在连接数不足时进行 2 倍扩容。
package util;
import java.util.ArrayList;
import java.util.List;
import java.sql.*;
/**
* ConnectPool 类为自建的可扩容的简易数据库连接池
* @author 林智凯
* @version 1.0
*/
public class ConnectPool {
private List<Connection> pool = new ArrayList<Connection>();
private Integer maxSize;
/**
* 数据库连接池的构造方法,预先分配5个Connection对象
*/
public ConnectPool() throws SQLException {
Integer num = 5;
this.maxSize = num;
for (int i = 0; i < num; i++) {
Connection conn = MysqlConnect.connectDatabase();
this.pool.add(conn);
}
}
/**
* 向数据库连接池获取连接资源,返回一个 Connection 对象
* @return Connection 对象
*/
public Connection getConnection() throws SQLException {
//检查是否还有Connection对象可以分配
if(pool.size() == 0){
//Connection对象不够用,先扩容
for (int i = 0; i < this.maxSize; i++) {
Connection conn = MysqlConnect.connectDatabase();
pool.add(conn);
}
//2倍扩容
this.maxSize *= 2;
}
//弹出第一个Connection对象返回
Connection conn = pool.remove(0);
return conn;
}
/**
* 回收 Connection 返回数据库连接池中。
* @param conn
*/
public void recoveryConnection(Connection conn){
pool.add(conn);
}
}
其中 MysqlConnect 类用于建立数据库连接的工具类,和 ConnectPool 类的关系用 UML 类图描述如下。
需要注意的是自建的 ConnectPool 类可能与 MySQL数据库的资源回收机制产生冲突,在特定的条件下连接会失效,因此建议使用连接池框架替换。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)