导航菜单
首页 >  layui在线考试  > 基于Jsp+Servlet的在线考试系统

基于Jsp+Servlet的在线考试系统

项目类型:JAVA WEB项目项目名称:在线考试系统用户类型:双角色(老师+学生)难度:3系统类型:后台系统设计模式:Jsp+Servlet开发工具:Eclipse 数据库:Mysql+Navicat数据库表:5张jsp页面:18个页面适用:软件工程、计算机科学与技术等JAVA课程的学习和实验,可以参考文中的部分代码,实现自己所需要的功能。

功能特点: 1.学生和老师登录,无注册(只能老师录入) 2.学生管理:添加、修改、删除、查询考生 3.题目管理:添加、修改、删除、查询题目,设置题目的分值,答案 4.考试设置:单选题、多选题、判断题、填空题、简答题 5.试卷设置:设置试卷的题型 6.学生考试:学生考试功能,和显示里的一样 7.学生信息管理:学生自己登录后可以修改密码

项目截图:

1.学生在线考试

2.学生个人信息 

3.教师登录

4.后台管理 

5.录入考试学生

 6.生成试卷

7.项目结构

 

 8.对于考题数量和考试时间的处理,Servlet类。获取前端Jsp页面输入的数据信息,并执行update语句,执行后将数据信息存储。 /** * @author*该类负责修改考题数量和考试时间 */@WebServlet("/PaperQuantity")public class PaperQuantityServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doPost(request, response);}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {request.setCharacterEncoding("UTF-8"); // 编码处理response.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=UTF-8");int id=Integer.valueOf(request.getParameter("id"));int time=Integer.valueOf(request.getParameter("time"));int qty_sing=Integer.valueOf(request.getParameter("qty_sing"));int qty_muti=Integer.valueOf(request.getParameter("qty_muti"));int qty_jud=Integer.valueOf(request.getParameter("qty_jud"));int qty_fill=Integer.valueOf(request.getParameter("qty_fill"));int qty_ess=Integer.valueOf(request.getParameter("qty_ess"));int quantity=qty_sing+qty_muti+qty_jud+qty_fill+qty_ess;try {DatabassAccessObject db = new DatabassAccessObject();db.modify("UPDATE paper SET time=?, qty_sing = ? , qty_muti = ? , qty_jud = ? , qty_fill=?, qty_ess=? , quantity = ? WHERE id = ? ;", time,qty_sing, qty_muti, qty_jud,qty_fill,qty_ess,quantity,id);PrintWriter out = response.getWriter();out.println("alert('已成功修改');window.location='" + request.getContextPath()+ "/teacher/teacher_paper_manage.jsp '");} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}  9.通过外部EXCEL表格,将考题导入。

 * 该类是自动抽提组卷类,实现功能:  * 1.每种题型随机抽取指定条数,例:选择题抽5条;  * 2.被抽中的题目信息传入QuestionBean储存;  * 3.把Bean存入动态数组ArrayList组成试卷数组;  * 4.把试卷数组存入Session对象,供考生作答。  */

/** * @author* 该类是自动抽提组卷类,实现功能: * 1.每种题型随机抽取指定条数,例:选择题抽5条; * 2.被抽中的题目信息传入QuestionBean储存; * 3.把Bean存入动态数组ArrayList组成试卷数组; * 4.把试卷数组存入Session对象,供考生作答。 */@WebServlet("/HandlePaper")public class QuestionExtractServlet extends HttpServlet {private static final long serialVersionUID = 1L;int tihao = 0;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doPost(request, response);}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {ArrayList listALL = new ArrayList();//抽题数量//单选题抽5道,多选题抽4道,判断题抽5道,填空题抽5道,简答题抽3道int num1=5,num2=4,num3=5,num4=5,num5=3;int examTime=30;try {DatabassAccessObject db = new DatabassAccessObject();ResultSet rs=db.query("select * from paper");num1=Integer.valueOf(rs.getString("qty_sing"));num2=Integer.valueOf(rs.getString("qty_muti"));num3=Integer.valueOf(rs.getString("qty_jud"));num4=Integer.valueOf(rs.getString("qty_fill"));num5=Integer.valueOf(rs.getString("qty_ess"));examTime=Integer.valueOf(rs.getString("time"));} catch (Exception e) {}try {DatabassAccessObject db = new DatabassAccessObject();ResultSet rs = db.query("SELECT * FROM question");//区分每种题型,将所有相同题型的行号存入双向循环链表,用于随机取题目LinkedList list1 = new LinkedList();LinkedList list2 = new LinkedList();LinkedList list3 = new LinkedList();LinkedList list4 = new LinkedList();LinkedList list5 = new LinkedList();while (rs.next()) {//遍历整个题库表switch (rs.getString(2)) {//多分支语句区分题型case "单选题":list1.add(rs.getRow());//获取所有选择题的行号break;case "多选题":list2.add(rs.getRow());break;case "判断题":list3.add(rs.getRow());break;case "填空题":list4.add(rs.getRow());break;case "简答题":list5.add(rs.getRow());break;default:break;}}listALL.addAll(randomQue(list1, rs,num1));listALL.addAll(randomQue(list2, rs,num2));listALL.addAll(randomQue(list3, rs,num3));listALL.addAll(randomQue(list4, rs,num4));listALL.addAll(randomQue(list5, rs,num5));tihao=0;//题号从0开始HttpSession session = request.getSession();session.setAttribute("examTime", examTime);session.setAttribute("ques", listALL);//把试卷数组保存到Session对象//重定向到试卷页面,供考生作答response.sendRedirect(request.getContextPath()+"/student/student_exam_paper.jsp");} catch (Exception e) {e.printStackTrace();}}

 * 负责每种题型、指定数量的随机抽题      * @param list    题目链表(存储了一类题型的行号)      * @param rs    数据表结果集      * @param count 抽取指定的题目数量      * @return    返回一个(存储了count条指定类型的题目)数组

/** * 负责每种题型、指定数量的随机抽题 * @param list题目链表(存储了一类题型的行号) * @param rs数据表结果集 * @param count 抽取指定的题目数量 * @return返回一个(存储了count条指定类型的题目)数组 */public ArrayList randomQue(LinkedList list,ResultSet rs,int count) {int m = -1;int index = -1;ArrayList listA = new ArrayList();while (list.size() > 0&&count>0) {count--;m = (int) (Math.random() * list.size());index = list.get(m);System.out.println(index);list.remove(m);tihao++;try {rs.absolute(index);QuestionBean queBean = new QuestionBean();queBean.setQ_id(String.valueOf(tihao));queBean.setQ_type(rs.getString(2));queBean.setQ_title(rs.getString(3));String selectString = rs.getString(4);System.out.println(rs.getString(2));queBean.setQ_score(rs.getString(5));queBean.setQ_key(rs.getString(6));queBean.setQ_img(rs.getString(7));if (selectString != null) {queBean.setQ_select(selectString);String[] temp = selectString.split("\\@");queBean.setOptions(temp);}listA.add(queBean);} catch (SQLException e) {e.printStackTrace();}}return listA;}}  10.对已有题目进行增删改查 /** * @author* 负责处理题库表中题目的增删改查 */@WebServlet("/HandleQue")@MultipartConfig //支持文件上传public class QuestionModifyServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request, response);}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.setCharacterEncoding("UTF-8"); // 编码处理response.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=UTF-8");String executeMode = request.getParameter("executeMode");int mode = Integer.parseInt(executeMode); // 转化整形System.out.println(mode);String number=request.getParameter("number");String type="";String title="";String score="";String key="";String select = "";String fileName ="";if (mode!=2) {//如果不用执行删除,就执行type=request.getParameter("type");title=request.getParameter("title");score=request.getParameter("score");key=request.getParameter("key");if (type.equals("单选题")||type.equals("多选题")) {String[] temp=request.getParameterValues("select");for (int i = 0; i < temp.length; i++) {select+=temp[i];if (i!=temp.length-1) {select+="@";}}}else {select=request.getParameter("select");}System.out.print(select);Part part = request.getPart("img"); //题目配图fileName = part.getSubmittedFileName(); // 获取part对象所携带的文件名称if (fileName.length() > 0) { //如果用户上传了题目配图String savePath = getServletContext().getRealPath("/images");part.write(savePath + "/" + fileName);//上传图片到发布目录下try {//try能保证文件流被正确关闭//图片的原始路径(tomcat的发布目录下)Path sorcePath = Paths.get(savePath + "/" + fileName); //图片的目标路径(当前项目目录)Path targetPath = Paths.get("E:\\WorkSpace_All\\cli_workspace\\"+request.getContextPath()+"/WebRoot/images/" + fileName);//复制文件并替换已存在的文件Files.copy(sorcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);} catch (Exception e) {}}}try {DatabassAccessObject db = new DatabassAccessObject();switch (mode) {case 0://增db.insert("insert into question values (?,?,?,?,?,?,?) ; ",number,type,title,select,score,key,fileName);break;case 1://改if (fileName!="") {db.modify("update question set type = ? , title = ? , `select` = ? , score = ? , `key` = ? , img = ? where number = ? ;",type,title,select,score,key,fileName,number);}else {db.modify("update question set type = ? , title = ? , `select` = ? , score = ? , `key` = ? where number = ? ;",type,title,select,score,key,number);}break;case 2://删db.modify("delete from question where number = ? ; ", number);break;default:break;}} catch (Exception e) {}if (mode==0) {PrintWriter out = response.getWriter();out.println ("window.location='"+request.getContextPath()+"/teacher/teacher_que_add.jsp';alert('已成功添加题目');");}elseresponse.sendRedirect("ShowQuePage");}}  11.对题目信息通过列表来分页查询 /** * @author * 分页显示题库表的题目信息 * 默认每页10条 */@WebServlet("/ShowQuePage")public class QuestionShowByPageServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 如果没有参数传递过来,初定每页显示10条记录,显示第一页。// 然后把这两个参数压到request容器传到前台页面。request容器是前台页面收到即销毁的容器。int cpage = 0;int per = 10;if (request.getParameter("cpage") != null) {cpage = Integer.parseInt(request.getParameter("cpage"));}request.setAttribute("cpage", cpage);if (request.getParameter("per") != null) {per = Integer.parseInt(request.getParameter("per"));}request.setAttribute("per", per);// 之后进行数据库的查询try {// 先数据库的查询结果有多少条记录DatabassAccessObject db = new DatabassAccessObject();ResultSet rsTotal = db.query("select count(*) as total from question");if (rsTotal.next()) {// 求出总页数压到request容器传递给前台页面。request.setAttribute("totalPage", 1 + (rsTotal.getInt("total") - 1) / per);}// 新建一个动态数组用来存放查询结果ArrayList queBeanAllList = new ArrayList();ResultSet rs=null;String q_title=request.getParameter("q_title");String q_type="";q_type=request.getParameter("q_type");if (q_title!=null) {rs=db.query("select * from question where title LIKE '%"+q_title+"%' ;");}else if(q_type!=null&&q_type.length()>0){rs=db.query("select * from question where type='"+q_type+"' ;");}else {rs = db.query("select\r\n" + "*\r\n" + "from\r\n" + "question\r\n" + "order by\r\n" + "case \r\n" + " when type='单选题' then 1\r\n" + " when type='多选题' then 2\r\n" + "when type='判断题' then 3\r\n" + "when type='填空题' then 4\r\n" + "when type='简答题' then 5\r\n" + "end");}// 所有查询结果存入数组int total = 0;while (rs.next()) {QuestionBean queBean = new QuestionBean();queBean.setQ_id(rs.getString("number"));queBean.setQ_type(rs.getString("type"));queBean.setQ_title(rs.getString("title"));String selectString = rs.getString("select");if (selectString != null) {queBean.setQ_select(selectString);String[] temp = selectString.split("\\@");queBean.setOptions(temp);}queBean.setQ_score(rs.getString("score"));queBean.setQ_key(rs.getString("key"));queBean.setQ_img(rs.getString("img"));queBeanAllList.add(queBean);total++;}ArrayList queTableList = new ArrayList();for (int i = cpage * per; i < cpage * per + per && i < total; i++) {queTableList.add(queBeanAllList.get(i));}String temp=request.getParameter("modify_id");request.setAttribute("modify_id", temp);request.setAttribute("total", total);request.setAttribute("queTableList", queTableList);request.getRequestDispatcher("/teacher/teacher_que_manage.jsp").forward(request, response);} catch (Exception e) {e.printStackTrace();}}}  12.考试情况的处理 /**  * @author   * 该类负责统计考生作答的分数;  * 每种题型分别记分,成绩存入数据库。  */ /** * @author* 该类负责统计考生作答的分数; * 每种题型分别记分,成绩存入数据库。 */@WebServlet("/CalScoreServlet")public class ScoreCalServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doPost(request, response);}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setCharacterEncoding("UTF-8");request.setCharacterEncoding("UTF-8");HttpSession session = request.getSession(true);if (session == null) {response.sendRedirect("login.jsp");}//获取先前存储到Session对象中的试卷题目@SuppressWarnings("unchecked")ArrayList ques = (ArrayList) session.getAttribute("ques");String stuAnsArr[] = null;//每种题型分值,初值均为0float score = 0;float score_muti = 0;float score_sing = 0;float score_jud = 0;float score_fill = 0;float score_ess = 0;for (int i = 0; i < ques.size(); ++i) {QuestionBean que = ques.get(i);stuAnsArr = request.getParameterValues(que.getQ_id());//获取每道题的答案//如果是多选题,存在多个选项值,因此需要getParameterValues方法获取多个值if (!que.getQ_type().equals("简答题")) {//非简答题的记分方式相同,简答题独立计算if (stuAnsArr != null) {String stuAns = ""; //每道题的答案for (int j = 0; j < stuAnsArr.length; j++) {//多选题拥有多个答案stuAns += stuAnsArr[j];//组装学生答案}System.out.println(stuAns);if (stuAns.equalsIgnoreCase(que.getQ_key())) {//匹配学生答案和正确答案switch (que.getQ_type()) { //每道题分别记分case "单选题":score_sing += Float.parseFloat(que.getQ_score());break;case "多选题":score_muti += Float.parseFloat(que.getQ_score());break;case "判断题":score_jud += Float.parseFloat(que.getQ_score());break;case "填空题":score_fill += Float.parseFloat(que.getQ_score());break;default:break;}}}} else { //简答题的判断方法String[] KEY_WORD = que.getQ_key().split("\\@");//拆分正确答案中的关键词Float totalScore = Float.parseFloat(que.getQ_score());//获取简答题分值Float singleScore = 0.0f;//每个的分点的细分String stuAns = "";if (stuAnsArr != null) {for (int j = 0; j < stuAnsArr.length; j++) {stuAns += stuAnsArr[j];//组装学生答案}}// 使用contains方法if (KEY_WORD != null) {//如果关键词存在singleScore = totalScore / KEY_WORD.length; //按照关键词数量细分分值for (int j = 0; j < KEY_WORD.length; j++) {if (stuAns.contains(KEY_WORD[j])) { //判断考生答案中是否出现关键词System.out.println(stuAns + "包含关键词:" + KEY_WORD[j]);score_ess += singleScore;} else {System.out.println("不包含关键词:" + KEY_WORD[j]);}}}}}score = score_sing + score_muti + score_jud + score_fill + score_ess;String grade = "";int f = Math.round(score);int g = ((f < 0) == true ? 1 : 0) + ((f < 60) == true ? 1 : 0) + ((f < 75) == true ? 1 : 0)+ ((f < 85) == true ? 1 : 0) + ((f < 95) == true ? 1 : 0);switch (g) {case 0:grade = "优秀";break;case 1:grade = "良好";break;case 2:grade = "中等";break;case 3:grade = "及格";break;case 4:grade = "不及格";break;case 5:grade = "缺考";break;default:break;}try {//实例化数据库连接对象,把上面计算到的分值分别存入数据库中DatabassAccessObject db = new DatabassAccessObject();LoginBean loginBean = (LoginBean) session.getAttribute("loginBean");String ID = loginBean.getID();db.insert("update student set score = ? where ID = ? ", score, ID);ResultSet rs = db.query("select * from score where id = ?", ID);if (!rs.next()) {db.insert("insert into score values(?,?,?,?,?,?,?,?)", ID, score, score_sing, score_muti, score_jud,score_fill, score_ess,grade);} else {db.modify("update score set score = ? , score_sing = ? , score_muti= ? , score_jud = ? , score_fill = ? , score_ess = ?,grade = ? where ID = ? ;",score, score_sing, score_muti, score_jud, score_fill, score_ess,grade, ID);}rs = db.query("select * from student where id = ?", ID);rs.first();} catch (Exception e) {e.printStackTrace();}System.out.println(score);response.setContentType("text/html;charset=utf-8");//如果考生交了白卷,且作答时间有剩余5分钟以上,则建议考生继续作答,重定向回试卷页面;//否则,视为交卷成功,重定向回学生信息页。PrintWriter out = response.getWriter();long curTime = System.currentTimeMillis() / 1000;long endTime = (long) session.getAttribute("endTime");if (score == 0 && endTime - curTime >= 300) {out.println("if(confirm('时间尚有剩余,请认真作答')){window.location='" + request.getContextPath()+ "/student/student_exam_paper.jsp';}");out.println("window.location='" + request.getContextPath()+ "/student/student.jsp';alert('试卷已经提交,可以查阅成绩单');");LoginBean loginBean = (LoginBean) session.getAttribute("loginBean");loginBean.setScore(score);} else {LoginBean loginBean = (LoginBean) session.getAttribute("loginBean");loginBean.setScore(score);session.removeAttribute("examTime");session.removeAttribute("endTime");out.println("window.location='" + request.getContextPath()+ "/student/student.jsp';alert('试卷已经提交,可以查阅成绩单');");}}}  13.分页查询考生的成绩 @WebServlet("/ShowScorePage")public class ScoreShowByPage extends HttpServlet {private static final long serialVersionUID = 1L;protected void service(HttpServletRequest request,HttpServletResponse response) throws ServletException {// 如果没有参数传递过来,初定每页显示10条记录,显示第一页。// 然后把这两个参数压到request容器传到前台页面。request容器是前台页面收到即销毁的容器。int cpage = 0;int per = 10;if (request.getParameter("cpage") != null) {cpage = Integer.parseInt(request.getParameter("cpage"));}request.setAttribute("cpage", cpage);if (request.getParameter("per") != null) {per = Integer.parseInt(request.getParameter("per"));}request.setAttribute("per", per);try {// 数据库的查询结果有多少条记录DatabassAccessObject db=new DatabassAccessObject();ResultSet rsTotal = db.query("select count(*) as total from student");if (rsTotal.next()) {// 求出总页数压到request容器传递给前台页面。request.setAttribute("totalPage", 1+ (rsTotal.getInt("total") - 1) / per);}// 新建一个动态数组用来存放查询结果ArrayList stuBeanAllList = new ArrayList();ResultSet rs=null;String s_ID=request.getParameter("s_ID");if (s_ID!=null) {rs=db.query("select score.ID,class,name,score_sing,score_muti,score_jud,score_fill,score_ess,score.score,grade from student join score on student.id=score.id where score.id LIKE '%"+s_ID+"%' ;");}else {rs = db.query("select score.ID,class,name,score_sing,score_muti,score_jud,score_fill,score_ess,score.score,grade from student join score on student.id=score.id order by ID ;");}int total = 0;while (rs.next()) {String ID=rs.getString(1);String CLASS=rs.getString(2);String name=rs.getString(3);Float score_sing=Float.valueOf(rs.getString(4));Float score_muti=Float.valueOf(rs.getString(5));Float score_jud=Float.valueOf(rs.getString(6));Float score_fill=Float.valueOf(rs.getString(7));Float score_ess=Float.valueOf(rs.getString(8));Float score=Float.valueOf(rs.getString(9));String grade=rs.getString(10);stuBeanAllList.add(new StudentInfoBean(ID, CLASS, name, score_sing, score_muti, score_jud, score_fill, score_ess, score,grade));total++;}// 再通过根据cpage与per求出要推回给前台显示的数组ArrayList stuTableList = new ArrayList();for (int i = cpage * per; i < cpage * per + per && i < total; i++) {stuTableList.add(stuBeanAllList.get(i));}String temp=request.getParameter("modify_id");System.out.println(temp);request.setAttribute("modify_id", temp);request.setAttribute("total", total);request.setAttribute("stuTableList", stuTableList);request.getRequestDispatcher("/teacher/teacher_score_manage.jsp").forward(request,response);} catch (Exception e) {e.printStackTrace();}}}  14.数据库连接配置

 *该类是数据库连接对象,实现功能:  *连接数据库,增加、删除、修改、查询数据。

/** **该类是数据库连接对象,实现功能: *连接数据库,增加、删除、修改、查询数据。 */public class DatabassAccessObject {private Connection con; /** * 构造函数,连接数据库 * @throws Exception */public DatabassAccessObject() throws Exception {String dburl = "jdbc:mysql://localhost:3306/jsp_examonline?serverTimezone=UTC&characterEncoding=utf8&useSSL=false";String dbusername = "root";String dbpassword = "123456";Class.forName("com.mysql.jdbc.Driver");this.con = DriverManager.getConnection(dburl, dbusername, dbpassword);} /** * 数据库查询 * @param sql 任意的SQL查询语句 * @param args 任意个用于替换占位符的形参 * @return返回RestultSet类型的结果集 * @throws Exception */public ResultSet query(String sql, Object... args) throws Exception {PreparedStatement ps = con.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}return ps.executeQuery();} /** * 向数据库插入一条数据 * @param sql任意的SQL插入语句 * @param args任意个用于替换占位符的形参 * @return返回值是布尔类型 * @throws Exception */public boolean insert(String sql, Object... args) throws Exception {PreparedStatement ps = con.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}if (ps.executeUpdate() != 1) {return false;}return true;} /** * 修改数据库中的数据 * @param sql任意的SQL更新语句 * @param args任意个用于替换占位符的形参 * @return返回值是布尔类型 * @throws Exception */public boolean modify(String sql, Object... args) throws Exception {PreparedStatement ps = con.prepareStatement(sql);for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]);}if (ps.executeUpdate() != 1) {return false;}return true;} // 析构函数,中断数据库的连接protected void finalize() throws Exception {if (!con.isClosed() || con != null) {con.close();}}} 15.student_exam.jsp页面

在线考试的jsp页面详情。

var second =(${sessionScope.endTime-curTime});var timer=null;var showTime = function(){var s = second % 60; // 秒var mi = (second - s) / 60 % 60; // 分钟var h = ((second - s) / 60 - mi ) / 60 % 24; // 小时return h + "时" + mi + "分" + s + "秒";}timer=setInterval(function(){second --;document.getElementById("times").innerHTML = showTime ();if(second${que.q_id}.   —${que.q_type}—>${que.q_id}.  —${que.q_type}— >${que.q_id}.  —${que.q_type}— >${que.q_id}. —${que.q_type}—>${que.q_id}. 

相关推荐: