1)创建题目表,我们要进行设计一下里面的字段和属性:
1.1)设置题目的序号:作为题目表的自增主键
1.2)题目的标题(两数之和)
1.3)题目的难度(简单,中等,困难)
1.4)题目的具体要求描述,也叫做题干,不同的题目,题目描述是不相同的
1.5)题目的给定的代码模板,给用户展示的初始代码用户要在这个代码模板的基础上面做题
1.6)题目的测试用例(不会进行展示给前端);
我们的这个datasource是单例模式的,只要有一个数据库,那么它就是单例模式的
create database if not exists NowOJ; use NowoJ; drop table if exists TitleList; create table TitleList( TitleID int primary key auto_increment, TitleData varchar(40), TitleLevel varchar(40), Description varchar(10000), PreJavaCode varchar(4096), TestCode varchar(4096) );
package MYSQL; 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 ConnectionMysql { private static final String url="jdbc:mysql://127.0.0.1:3306/NowOJ?characterEncoding=utf-8&userSSL=true"; private static final String user="root"; private static final String password="12503487"; private static volatile DataSource dataSource=null; private static DataSource GetDataSource() { if(dataSource==null){ synchronized (Object.class){ if(dataSource==null) { dataSource=new MysqlDataSource(); ((MysqlDataSource)dataSource).setURL(url); ((MysqlDataSource)dataSource).setPassword(password); ((MysqlDataSource)dataSource).setUser(user); } } } return dataSource; } public Connection GetConnection() throws SQLException { return GetDataSource().getConnection(); } public static void close(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet) throws SQLException { if(resultSet!=null) { resultSet.close(); } if(preparedStatement!=null) { preparedStatement.close(); } if(connection!=null) { connection.close(); } } }
创建一个实体类对象:
实体类:一个实体类对象就对应着表中的一条记录,一个实体类的一个属性对应着一张表的一条记录,并生成Getter和Setter,ToString方法,我们的Java代码的字段是和数据库中的表的字段是相同的;
我们在这里面要实现的功能是:
1)新增题目(参数直接给定一个题目)
2)删除题目(根据题目ID来进行删除)
3)进行查询题目列表,进行给题目列表页来进行服务的,在我们的题目列表页里面,我们只需要进行获取三条数据即可,题目ID,题目名字还有题目的难易程度;
4)查询题目详情,根据题目ID来进行查询,所有有关于题目的信息都要进行查询;
在这里面,不同的用户的操作权限是不一样的,我们的管理员是负责进行删除和新增题目,而我们的普通用户只进行负责查
我们在这里面一定要注意一个事情:
1)我们在数据库里面进行查找题目列表操作,主要是给题目列表页来进行使用的,我们进行查找的时候,直接用select *.....,这种查询方法就会太粗暴了,数据库往往是最重要的一个模块,同时往往也是最脆弱的模块,数据库里面会存放很多很多数据,这些数据都是存放在磁盘上面的,当我们进行一些大规模操作的时候,如果进行select *操作,如果说表比较大,那么有可能直接把数据库服务器给卡死了,况且网络带宽就被吃满了,客户端和服务器是通过网络来实现数据交互的,磁盘IO基本被吃满,磁盘上面查数据很麻烦
2)如果说我们要是有大量的数据的话,我们可以使用分页查询来进行查找,我们是可以根据前端传递过来的页数来进行分页查询的,比如说我们一页有50条数据,假设我们现在用户点击的页码是三,那么此时的offset就应该是100
(页数*一页中的条数)-1;
我们这是就可以根据前端传递过来的页码,根据页码进行算一下,根据sql limit offset语句,要算出来offset是多少
1--->0-49
2--->50-99
3--->100-149
3)我们在与数据库建立连接的时候,最终是要进行关闭资源的,那么我们可不可以把关闭语句写道try语句里面呢?一旦我们的try里面的语句出现了问题,发生了异常,那么我们的程序就很有可能直接跳转到catch语句里面了,就可能无法执行到关闭语句了,所以我们的关闭操作一定要放到finally里面;
package MYSQL; import com.mysql.jdbc.MySQLConnection; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class OperateTitle { public void insert(OJTitle ojTitle) throws SQLException { //1与数据库建立连接 Connection connection=ConnectionMysql.GetConnection(); //2构建SQL语句 String SQL="insert into TitleList values(null,?,?,?,?,?)"; PreparedStatement statement= connection.prepareStatement(SQL); statement.setString(1,ojTitle.getTitleData()); statement.setString(2,ojTitle.getTitleLevel()); statement.setString(3,ojTitle.getDescription()); statement.setString(4,ojTitle.getPreJavaCode()); statement.setString(5,ojTitle.getTestCode()); //3进行执行SQL int len=statement.executeUpdate(); if(len==1) { System.out.println("一道题插入成功"); }else{ System.out.println("一道题插入失败"); } ConnectionMysql.close(connection,statement,null); } public void delete(int TitleID) throws SQLException { Connection connection= ConnectionMysql.GetConnection(); String SQL="delete from TitleList where TitleID=?"; PreparedStatement statement= connection.prepareStatement(SQL); statement.setInt(1,TitleID); int len=statement.executeUpdate(); if(len==1) { System.out.println("删除成功"); }else{ System.out.println("删除失败"); } ConnectionMysql.close(connection,statement,null); } public ListselectAll() throws SQLException { //1与数据库建立连接 Listlist=new ArrayList<>(); Connection connection=ConnectionMysql.GetConnection(); //2进行拼装SQL语句 String SQL="select * from TitleList"; PreparedStatement statement=connection.prepareStatement(SQL); ResultSet resultSet= statement.executeQuery(); while(resultSet.next()) { OJTitle ojTitle=new OJTitle(); ojTitle.setTitleID(resultSet.getInt("TitleID")); ojTitle.setTitleData(resultSet.getString("TitleData")); ojTitle.setTitleLevel(resultSet.getString("TitleLevel")); ojTitle.setDescription(resultSet.getString("PreJavaCode")); ojTitle.setTestCode(resultSet.getString("TestCode")); list.add(ojTitle); } ConnectionMysql.close(connection,statement,null); System.out.println("查询所有成功"); return list; } public OJTitle selectOne(int TitleID) throws SQLException { 由于题目列表是自增主键,我们查询到的结果一定只有一条数据 Connection connection=ConnectionMysql.GetConnection(); String SQL="select * from titlelist where TitleID=?"; PreparedStatement statement=connection.prepareStatement(SQL); statement.setInt(1,TitleID); ResultSet resultSet= statement.executeQuery(); System.out.println(resultSet); OJTitle ojTitle=new OJTitle(); while(resultSet.next()) { ojTitle.setTitleID(resultSet.getInt("TitleID")); ojTitle.setTitleData(resultSet.getString("TitleData")); ojTitle.setTitleLevel(resultSet.getString("TitleLevel")); ojTitle.setDescription(resultSet.getString("PreJavaCode")); ojTitle.setTestCode(resultSet.getString("TestCode")); } ConnectionMysql.close(connection,statement,null); System.out.println("查询一个成功"); return ojTitle; } public static void main(String[] args) throws SQLException { OJTitle ojTitle=new OJTitle(); OperateTitle operateTitle=new OperateTitle(); // ojTitle.setTitleData("A"); // ojTitle.setTitleLevel("OK"); // ojTitle.setDescription("please write"); // ojTitle.setTestCode("psvm"); // ojTitle.setTestCode("HH"); // operateTitle.insert(ojTitle); // Listlist= operateTitle.selectAll(); // OJTitle ojTitle1= operateTitle.selectOne(1); // System.out.println(list); // System.out.println(ojTitle1); // operateTitle.delete(1); } }注意:MYSQL在进行指定字段进行查询的时候,不要写成:
select (username,password) from user,不要加括号
为了验证上述我们所写的数据库模块是否有问题,我们进行设计测试用例并进行验证
1)其实当我们在进行构造数据库数据的时候,其他字段都好办,但是测试用例代码不好处理,因为测试用例代码我们无法从leetcode拷贝过来的
2)我们用户提交的代码是一个类,里面包含了方法,我们进行设计测试用例的时候,就要创建main方法,调用我们所的写的前端在线提交的OJ代码,然后进行执行
3)其实我们在设计测试用例的时候,就是一个main方法,但是在这个main方法里面,我们会创建Solution的实例,并调用里面的核心方法(twoSum)(做题人自己写的方法代码),当调用核心方法(里面的逻辑是我们是我们自己写的)的时候,传入不同的参数,并针对返回结果做不同的判定,如果说返回结果符合预期,那么就打印”TestOK“,如果不符合预期,那么就进行打印Test failed,还打印出出错的详情
3)在我们的服务器里面,我们会收到Solution类的完整实现代码,此时用户提交的代码没有main方法,我们在从数据库中查找到对应的测试用例代码,将两个代码进行字符串拼接,此时这个Solution就有main方法了,我们就可以单独的进行编译和运行了,此时的这个字符串就是我们前面写的Question类中的字符串,就是preJavaCode字段
用户提交的代码: package MYSQL; public class Solution { public int[] twoSum(int nums[],int target) { //这里面存放用户自己写的代码 } } 我们所写的测试用例的方法: public static void main(String[] args) { int[] nums={2,7,11,15}; int target=9; Solution solution=new Solution(); int[] arr1= solution.twoSum(nums,target); if(arr1.length==2&&arr1[0]==0&&arr1[1]==1) { System.out.println("Test OK"); }else{ System.out.println("Test failed"); } int[] nums1={3,3,6}; target=6; int[] arr2= solution.twoSum(nums1,target); if(arr2.length==2&&arr2[0]==1&&arr2[1]==2) { System.out.println("Test OK"); }else{ System.out.println("Test failed"); } }我们要将上述的测试代码和用户进行提交的代码进行拼接,然后执行
此时进行测试的代码只是将最核心的流程跑了一遍,而没有进行考虑更多的细节,这就被称之为冒烟测试,我们只测试了这几种方法(增删查改);这几个方法对了,就说明代码核心流程功能没有太大问题,这就类似于以前工程中用的板子,板子焊接好之后,只要他不冒烟,先别说他焊的对不对,但是从大致流程上面看是没有问题的;
但是从另一个角度来说,我们此时有针对每一个方法进行测试,这又被称为单元测试,发现问题越早问题就好解决;