上篇文件的改进
大家可以像我一样把类分类装包,这样更清晰
1.设计数据库
数据库的作用是把当前的题目信息保存到数据库中,
我们先设计题目表。然后分析,数据库到底需要哪些内容。
以上是数据库实现但是在项目里面我们要进行ojbc编程,我们需要用代码语言来操作数据库,这就是上一章我们要用maven导入数据库的目的。
用以下代码把数据库操作放进去。下面就是ojbc里面常用的连接模式。个人感觉不需要记住把。记住流程即可。
package common;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DBUtil {
// 需要封装和数据库之间的连接操作.
//下面这个表示我们要连接的数据库是谁。
//端口号默认3306
private static final String URL = "jdbc:mysql://127.0.0.1:3306/oj_database?characterEncoding=utf8&useSSL=false";
private static final String USERNAME = "root";
// private static final String PASSWORD = "2222";
private static final String PASSWORD = "111111";
//volatile我们加了一个锁因为害怕我们第一次访问的时候线程不安全。
private static volatile DataSource dataSource = null;
//这里用到的是懒汉模式:当程序访问实例才创建:
//饿汉模式的意思是启动时就创建
private static DataSource getDataSource() {
if (dataSource == null) {
synchronized (DBUtil.class) {
if (dataSource == null) {
MysqlDataSource mysqlDataSource = new MysqlDataSource();
mysqlDataSource.setURL(URL);
mysqlDataSource.setUser(USERNAME);
mysqlDataSource.setPassword(PASSWORD);
dataSource = mysqlDataSource;
}
}
}
return dataSource;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
//关闭释放资源
//安装反序释放
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2. dao包
problem类
我们创建一个实体表,一个实体类对象。DAO,下面创建一个实体类对象,problem就对应一个题,然后我们还要进行增删改查,就创建一个ProblemDAO来负责进行增删查改。
在problem中我们要生成get,set,tostring的方法。
package dao;
//操作数据库的类
public class Problem {
private int id;
private String title;
private String level;
private String description;
private String templateCode;
private String testCode;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTemplateCode() {
return templateCode;
}
public void setTemplateCode(String templateCode) {
this.templateCode = templateCode;
}
public String getTestCode() {
return testCode;
}
public void setTestCode(String testCode) {
this.testCode = testCode;
}
@Override
public String toString() {
return "Problem{" +
"id=" + id +
", title='" + title + '\'' +
", level='" + level + '\'' +
", description='" + description + '\'' +
", templateCode='" + templateCode + '\'' +
", testCode='" + testCode + '\'' +
'}';
}
problemdao类
这个就是对题目增删查改的类,
1.管理员负责新增和删除。
2.普通用户主要负责查
这里面最难的点就在于每一个题的测试用例很难这个需要我们自己设计。
这里将测试用例,整理成一个main方法,在这个main中,会创建一个Solution实例,main方法的核心点就是,两数相加。并且调用里面提供的核心方法,传入不同参数时,返回不同的值。
在服务器中,会收到用户提交的Solution类的完整实现代码。然后从数据库中查询到相应的测试用例代码。
package dao;
import common.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
// 通过这个类封装了针对 Problem 的增删改查.
// 1. 新增题目
// 2. 删除题目
// 3. 查询题目列表
// 4. 查询题目详情
public class ProblemDAO {
public void insert(Problem problem) {
Connection connection = null;
PreparedStatement statement = null;
try {
// 1. 和数据库建立连接
connection = DBUtil.getConnection();
// 2. 构造 SQL 语句
String sql = "insert into oj_table values(null, ?, ?, ?, ?, ?)";
statement = connection.prepareStatement(sql);
statement.setString(1, problem.getTitle());
statement.setString(2, problem.getLevel());
statement.setString(3, problem.getDescription());
statement.setString(4, problem.getTemplateCode());
statement.setString(5, problem.getTestCode());
// 3. 执行 SQL
int ret = statement.executeUpdate();
if (ret != 1) {
System.out.println("题目新增失败!");
} else {
System.out.println("题目新增成功!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, null);
}
}
public void delete(int id) {
Connection connection = null;
PreparedStatement statement = null;
try {
// 1. 和数据库建立连接
connection = DBUtil.getConnection();
// 2. 拼装 SQL 语句
String sql = "delete from oj_table where id = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1, id);
// 3. 执行 SQL
int ret = statement.executeUpdate();
if (ret != 1) {
System.out.println("删除题目失败!");
} else {
System.out.println("删除题目成功!");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtil.close(connection, statement, null);
}
}
// 这个操作是把当前题目列表中的所有题都查出来了
// 万一数据库中的题目特别多, 咋办? 只要实现 "分页查询" 即可. 后台实现分页查询, 非常容易.
// 前端传过来一个当前的 "页码" , 根据页码算一下, 依据 sql limit offset 语句, 要算出来 offset 是 几
// 但是前端这里实现一个分页器稍微麻烦一些(比后端要麻烦很多). 此处暂时不考虑分页功能.
public List<Problem> selectAll() {
List<Problem> problems = new ArrayList<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// 1. 和数据库建立连接
connection = DBUtil.getConnection();
// 2. 拼装 SQL
String sql = "select id, title, level from oj_table";
statement = connection.prepareStatement(sql);
// 3. 执行 SQL
resultSet = statement.executeQuery();
// 4. 遍历 resultSet
while (resultSet.next()) {
// 每一行都是一个 Problem 对象
Problem problem = new Problem();
problem.setId(resultSet.getInt("id"));
problem.setTitle(resultSet.getString("title"));
problem.setLevel(resultSet.getString("level"));
problems.add(problem);
}
return problems;
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return null;
}
public Problem selectOne(int id) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// 1. 和数据库建立连接
connection = DBUtil.getConnection();
// 2. 拼接 SQL 语句
String sql = "select * from oj_table where id = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1, id);
// 3. 执行 SQL
resultSet = statement.executeQuery();
// 4. 遍历查询结果. (由于 id 是主键, 按照 id 查找的结果一定是唯一的)
if (resultSet.next()) {
Problem problem = new Problem();
problem.setId(resultSet.getInt("id"));
problem.setTitle(resultSet.getString("title"));
problem.setLevel(resultSet.getString("level"));
problem.setDescription(resultSet.getString("description"));
problem.setTemplateCode(resultSet.getString("templateCode"));
problem.setTestCode(resultSet.getString("testCode"));
return problem;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return null;
}
private static void testInsert() {
ProblemDAO problemDAO = new ProblemDAO();
Problem problem = new Problem();
// problem.setId();
problem.setTitle("两数之和");
problem.setLevel("简单");
problem.setDescription("给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。\n" +
"\n" +
"你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。\n" +
"\n" +
"你可以按任意顺序返回答案。\n" +
"\n" +
" \n" +
"\n" +
"示例 1:\n" +
"\n" +
"输入:nums = [2,7,11,15], target = 9\n" +
"输出:[0,1]\n" +
"解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。\n" +
"示例 2:\n" +
"\n" +
"输入:nums = [3,2,4], target = 6\n" +
"输出:[1,2]\n" +
"示例 3:\n" +
"\n" +
"输入:nums = [3,3], target = 6\n" +
"输出:[0,1]\n" +
" \n" +
"\n" +
"提示:\n" +
"\n" +
"2 <= nums.length <= 104\n" +
"-109 <= nums[i] <= 109\n" +
"-109 <= target <= 109\n" +
"只会存在一个有效答案\n" +
"进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?\n" +
"\n" +
"来源:力扣(LeetCode)\n" +
"链接:https://leetcode-cn.com/problems/two-sum\n" +
"著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。");
problem.setTemplateCode("class Solution {\n" +
" public int[] twoSum(int[] nums, int target) {\n" +
"\n" +
" }\n" +
"}");
problem.setTestCode(" public static void main(String[] args) {\n" +
" Solution solution = new Solution();\n" +
" // testcase1\n" +
" int[] nums = {2,7,11,15};\n" +
" int target = 9;\n" +
" int[] result = solution.twoSum(nums, target);\n" +
" if (result.length == 2 && result[0] == 0 && result[1] == 1) {\n" +
" System.out.println(\"testcase1 OK\");\n" +
" } else {\n" +
" System.out.println(\"testcase1 failed!\");\n" +
" }\n" +
"\n" +
" // testcase2\n" +
" int[] nums2 = {3,2,4};\n" +
" int target2 = 6;\n" +
" int[] result2 = solution.twoSum(nums2, target2);\n" +
" if (result2.length == 2 && result[0] == 1 && result[1] == 2) {\n" +
" System.out.println(\"testcase2 OK\");\n" +
" } else {\n" +
" System.out.println(\"testcase2 failed!\");\n" +
" }\n" +
" }\n");
problemDAO.insert(problem);
System.out.println("插入成功!");
}
private static void testSelectAll() {
ProblemDAO problemDAO = new ProblemDAO();
List<Problem> problems = problemDAO.selectAll();
System.out.println(problems);
}
private static void testSelectOne() {
ProblemDAO problemDAO = new ProblemDAO();
Problem problem = problemDAO.selectOne(1);
System.out.println(problem);
}
private static void testDelete() {
ProblemDAO problemDAO = new ProblemDAO();
problemDAO.delete(1);
}
public static void main(String[] args) {
testInsert();
// testSelectAll();
// testSelectOne();
// testDelete();
}
}
这下就把数据库相关的操作封装好了,接下来可以设计服务器提供的API。一些HTTP风格的接口,通过这些接口和前端进行交互。
设计网页
思考要设计哪些网页(有几个网页都是干啥的)
题目列表页
功能就是展示当前的题目模板,向服务器请求。
题目详情页
功能分析
功能1;展示题目的详细要求,向服务器请求,获取指定题目的信息.
功能2;能够有一个代码编辑框,让用户来编写代码。
功能3;有一个提交按钮,点击按钮,就可以把代码发到服务器上,服务器编译运行后,返回结果。
现在流行的前后端交互方式通过JSON来组织的。所以我们引入jaskson
因此得到以下代码。
package api;
import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Problem;
import dao.ProblemDAO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet("/problem")
public class ProblemServlet extends HttpServlet {
//json核心的类
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(200);
resp.setContentType("application/json;charset=utf8");
ProblemDAO problemDAO = new ProblemDAO();
// 尝试获取 id 参数. 如果能获取到, 说明是获取题目详情; 如果不能获取到, 说明是获取题目列表.
String idString = req.getParameter("id");
if (idString == null || "".equals(idString)) {
// 没有获取到 id 字段. 查询题目列表
List<Problem> problems = problemDAO.selectAll();
String respString = objectMapper.writeValueAsString(problems);
//设置body部分
resp.getWriter().write(respString);
} else {
// 获取到了题目 id. 查询题目详情
Problem problem = problemDAO.selectOne(Integer.parseInt(idString));
String respString = objectMapper.writeValueAsString(problem);
resp.getWriter().write(respString);
}
}
}
然后再创建一个新的类CompileServlet,为了实现向服务器发送用户当前写的代码,并获得结果。