基于JSP+Servlet的在线学生选课管理系统 - 源码深度解析

JavaJavaScriptHTMLCSSMySQLJSP+Servlet
2026-02-194 浏览

文章摘要

本项目是一款基于JSP与Servlet技术栈构建的在线学生选课管理系统,旨在解决传统人工选课流程中存在的效率低下、信息不透明与易出错等核心痛点。系统通过业务流程的数字化重构,将课程发布、学生选课、名额控制与信息查询等环节整合于一体,显著提升了教务管理的规范性与学生操作的便捷性。 在技术实现上,系统...

在高等教育信息化进程中,教务管理系统的现代化转型是提升教学管理效率的关键环节。传统的纸质选课流程存在操作繁琐、信息滞后、易出错等痛点,亟需一套高效、稳定、易用的数字化解决方案。本系统采用经典的JSP与Servlet技术栈,构建了一个功能完备的在线选课管理平台,实现了从课程发布、学生选课到成绩管理的全流程数字化管控。

系统采用浏览器/服务器架构,严格遵循MVC设计模式。Servlet作为核心控制器,负责拦截和处理所有HTTP请求,进行业务逻辑调度和数据持久化操作;JSP页面专注于视图渲染,通过JSTL标签和EL表达式实现数据动态展示;业务逻辑层由独立的JavaBean组件构成,确保业务规则与Web层解耦。数据持久层基于JDBC直接操作MySQL数据库,通过数据库事务机制保障数据一致性。

数据库架构设计精要

系统数据库包含9张核心表,通过精妙的字段设计和外键关联构建了完整的数据模型。以课程表(course)和学生选课表(selected_course)为例,展现了严谨的数据结构设计。

课程表采用自增主键和唯一约束确保数据完整性:

CREATE TABLE `course` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `teacher_id` int(11) NOT NULL,
  `max_student_num` int(11) DEFAULT '50',
  `info` varchar(255) DEFAULT NULL,
  `selected_num` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`),
  KEY `teacher_id` (`teacher_id`),
  CONSTRAINT `course_ibfk_1` FOREIGN KEY (`teacher_id`) 
  REFERENCES `teacher` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

该设计亮点在于:max_student_num字段预设默认值50,通过selected_num实时统计已选人数;外键约束确保课程与教师数据的参照完整性;自增主键提升插入性能。

学生选课表设计体现了业务规则的数据化表达:

CREATE TABLE `selected_course` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `student_id` int(11) NOT NULL,
  `course_id` int(11) NOT NULL,
  `grade` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `student_course_unique` (`student_id`,`course_id`),
  KEY `course_id` (`course_id`),
  CONSTRAINT `selected_course_ibfk_1` FOREIGN KEY (`student_id`) 
  REFERENCES `student` (`id`) ON DELETE CASCADE,
  CONSTRAINT `selected_course_ibfk_2` FOREIGN KEY (`course_id`) 
  REFERENCES `course` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

联合唯一索引student_course_unique防止学生重复选课,级联删除约束确保数据一致性,grade字段允许空值适应成绩录入的异步性。

核心业务逻辑实现

选课事务控制机制 选课操作涉及课程余量检查和选课记录插入的原子性操作,通过JDBC事务确保数据一致性:

public boolean addSelectedCourse(SelectedCourse selectedCourse) throws SQLException {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    
    try {
        conn = DatabaseUtil.getConnection();
        conn.setAutoCommit(false); // 开启事务
        
        // 检查课程是否已满
        String checkSql = "SELECT selected_num, max_student_num FROM course WHERE id = ?";
        ps = conn.prepareStatement(checkSql);
        ps.setInt(1, selectedCourse.getCourseId());
        rs = ps.executeQuery();
        
        if (rs.next()) {
            int selectedNum = rs.getInt("selected_num");
            int maxNum = rs.getInt("max_student_num");
            if (selectedNum >= maxNum) {
                return false; // 课程已满
            }
        }
        
        // 插入选课记录
        String insertSql = "INSERT INTO selected_course(student_id, course_id) VALUES(?, ?)";
        ps = conn.prepareStatement(insertSql);
        ps.setInt(1, selectedCourse.getStudentId());
        ps.setInt(2, selectedCourse.getCourseId());
        int result = ps.executeUpdate();
        
        // 更新课程已选人数
        String updateSql = "UPDATE course SET selected_num = selected_num + 1 WHERE id = ?";
        ps = conn.prepareStatement(updateSql);
        ps.setInt(1, selectedCourse.getCourseId());
        ps.executeUpdate();
        
        conn.commit(); // 提交事务
        return result > 0;
        
    } catch (SQLException e) {
        if (conn != null) conn.rollback(); // 回滚事务
        throw e;
    } finally {
        DatabaseUtil.close(conn, ps, rs);
    }
}

基于Servlet的请求路由机制 前端控制器模式统一处理HTTP请求,通过路径映射实现方法分发:

@WebServlet("/course/*")
public class CourseServlet extends HttpServlet {
    private CourseDao courseDao = new CourseDaoImpl();
    
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        
        String pathInfo = request.getPathInfo();
        if ("/list".equals(pathInfo)) {
            listCourses(request, response);
        } else if ("/detail".equals(pathInfo)) {
            showCourseDetail(request, response);
        } else {
            // 默认处理逻辑
        }
    }
    
    private void listCourses(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        
        List<Course> courses = courseDao.findAll();
        request.setAttribute("courseList", courses);
        RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/jsp/course/list.jsp");
        dispatcher.forward(request, response);
    }
}

动态条件查询构建 课程查询功能支持多条件组合查询,动态构建SQL语句:

public List<Course> findCoursesByCondition(String courseName, Integer teacherId, Boolean hasCapacity) {
    StringBuilder sql = new StringBuilder("SELECT * FROM course WHERE 1=1");
    List<Object> params = new ArrayList<>();
    
    if (courseName != null && !courseName.trim().isEmpty()) {
        sql.append(" AND name LIKE ?");
        params.add("%" + courseName + "%");
    }
    
    if (teacherId != null) {
        sql.append(" AND teacher_id = ?");
        params.add(teacherId);
    }
    
    if (hasCapacity != null) {
        if (hasCapacity) {
            sql.append(" AND selected_num < max_student_num");
        } else {
            sql.append(" AND selected_num >= max_student_num");
        }
    }
    
    return jdbcTemplate.query(sql.toString(), params.toArray(), new CourseRowMapper());
}

JSP页面数据渲染 使用JSTL标签库实现数据的优雅展示,避免脚本片段污染:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<table class="table table-striped">
    <thead>
        <tr>
            <th>课程名称</th>
            <th>授课教师</th>
            <th>已选人数</th>
            <th>最大容量</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach var="course" items="${courseList}">
            <tr>
                <td>${course.name}</td>
                <td>${course.teacher.name}</td>
                <td>${course.selectedNum}</td>
                <td>${course.maxStudentNum}</td>
                <td>
                    <c:choose>
                        <c:when test="${course.selectedNum < course.maxStudentNum}">
                            <button class="btn btn-primary" onclick="selectCourse(${course.id})">选课</button>
                        </c:when>
                        <c:otherwise>
                            <button class="btn btn-secondary" disabled>已满</button>
                        </c:otherwise>
                    </c:choose>
                </td>
            </tr>
        </c:forEach>
    </tbody>
</table>

用户身份验证与会话管理 基于Session的登录状态维护机制:

public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String userType = request.getParameter("userType");
        
        User user = userService.authenticate(username, password, userType);
        if (user != null) {
            HttpSession session = request.getSession();
            session.setAttribute("currentUser", user);
            session.setAttribute("userType", userType);
            
            // 根据用户类型重定向到不同主页
            String redirectPath = getRedirectPath(userType);
            response.sendRedirect(redirectPath);
        } else {
            request.setAttribute("error", "用户名或密码错误");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }
}

系统功能模块展示

课程管理界面 教务人员通过课程管理模块维护课程信息,支持课程的新增、编辑、删除操作。界面清晰展示课程容量状态,便于监控选课情况。

课程管理

学生选课界面 学生用户可浏览可选课程列表,系统实时显示课程余量状态。选课操作具有防重复提交和容量校验机制,确保选课过程的公平性。

选课管理

成绩管理模块 教师通过成绩管理界面为所选课程的学生录入成绩,支持成绩的批量导入和导出功能。

成绩管理

性能优化与扩展方向

数据库查询优化 对高频查询操作建立复合索引,提升数据检索效率:

CREATE INDEX idx_course_teacher ON course(teacher_id, selected_num);
CREATE INDEX idx_selected_course_student ON selected_course(student_id, course_id);

缓存策略实施 引入Redis缓存层,缓存课程列表、学生课表等相对静态的数据:

public List<Course> getAvailableCourses() {
    String cacheKey = "available_courses";
    List<Course> courses = redisTemplate.opsForValue().get(cacheKey);
    
    if (courses == null) {
        courses = courseDao.findAvailableCourses();
        redisTemplate.opsForValue().set(cacheKey, courses, Duration.ofMinutes(30));
    }
    
    return courses;
}

异步处理机制 对于选课高峰期的并发请求,引入消息队列实现异步处理:

@JmsListener(destination = "course.selection.queue")
public void processCourseSelection(SelectionMessage message) {
    // 异步处理选课逻辑,缓解数据库压力
    courseService.processSelectionAsync(message);
}

前端技术升级 逐步采用Vue.js或React等现代前端框架替代传统JSP,实现前后端分离架构:

// Vue组件示例
const CourseSelection = {
    template: `
        <div>
            <course-list :courses="availableCourses" @select="handleSelect"/>
            <selection-cart :selectedCourses="selectedCourses"/>
        </div>
    `,
    data() {
        return {
            availableCourses: [],
            selectedCourses: []
        }
    },
    methods: {
        async loadCourses() {
            const response = await axios.get('/api/courses/available');
            this.availableCourses = response.data;
        },
        async handleSelect(courseId) {
            await axios.post('/api/selections', { courseId });
            this.selectedCourses.push(courseId);
        }
    }
};

微服务架构演进 将单体应用拆分为课程服务、用户服务、选课服务等微服务单元,提升系统可扩展性和维护性:

// 选课服务独立部署
@RestController
public class SelectionController {
    @PostMapping("/selections")
    public ResponseEntity<?> createSelection(@RequestBody SelectionRequest request) {
        selectionService.createSelection(request);
        return ResponseEntity.ok().build();
    }
}

该系统通过严谨的架构设计和精细的业务逻辑实现,为教育机构提供了稳定可靠的选课管理解决方案。基于经典技术栈的深度优化和现代化扩展路径规划,确保了系统在保持稳定性的同时具备持续演进的能力。

本文关键词
JSPServlet在线选课管理系统源码解析数据库设计

上下篇

上一篇
没有更多文章
下一篇
没有更多文章