一、实验概述
实验信息
课程:Web编程技术
实验项目:基于SpringMVC+Hibernate框架的学生选课分页显示页面设计
环境:Windows11、Eclipse、JDK、Tomcat9、MySQL
技术栈:SpringMVC + Hibernate + JSTL + MySQL
功能:查询学生选课表,每页展示6条数据,实现上下页切换分页
实验目的
- 掌握MySQL建库建表、批量插入测试数据;
- 熟悉SpringMVC请求分发、视图解析整套流程;
- 掌握Hibernate实体映射、原生分页API;
- 理解DAO-Service-Controller三层分层开发;
- 使用JSTL标签库渲染表格与分页控件;
- 排查XML配置、Jar缺失、数据库连接各类服务器异常。
项目目录结构
StudentCourseSystem
├─ src
│ └─ main
│ ├─ java
│ │ └─ com
│ │ ├─ entity // 实体类+hbm映射
│ │ ├─ util // Hibernate工具类
│ │ ├─ dao // 数据访问层
│ │ ├─ service // 业务层
│ │ └─ controller // 控制器
│ └─ resources // hibernate.cfg.xml、springmvc-servlet.xml
└─ WebContent└─ WEB-INF├─ lib // 全部依赖Jar包├─ jsp // courseList.jsp└─ web.xml
二、数据库设计与数据准备
1 创建数据库与表
CREATE DATABASE student_course_db DEFAULT CHARACTER SET utf;
USE student_course_db;
CREATE TABLE course_selection (id INT AUTO_INCREMENT PRIMARY KEY,student_id VARCHAR(20) COMMENT '学号',course_id VARCHAR(20) COMMENT '课程号',course_name VARCHAR(50) COMMENT '课程名',course_hours INT COMMENT '学时数',teacher_name VARCHAR(20) COMMENT '任课教师名',location VARCHAR(50) COMMENT '授课地点',academic_year VARCHAR(10) COMMENT '学年',semester INT COMMENT '学期'
);
2 插入30条测试数据
INSERT INTO course_selection (student_id, course_id, course_name, course_hours, teacher_name, location, academic_year, semester) VALUES
('209921123000', 'CS101', 'Web编程技术', 48, '余元辉', '机房301', '2025-2026', 2),
('209921123000', 'CS102', '数据结构', 64, '张老师', '教楼405', '2025-2026', 2),
('209921123000', 'CS103', '计算机网络', 48, '王老师', '教楼302', '2025-2026', 2),
('209921123000', 'CS104', '操作系统', 64, '刘老师', '机房202', '2025-2026', 2),
('209921123000', 'CS105', '软件工程', 32, '陈老师', '教楼501', '2025-2026', 2),
('209921123000', 'CS106', '编译原理', 48, '赵老师', '机房403', '2025-2026', 2),
('209921123001', 'CS101', 'Web编程技术', 48, '余元辉', '机房301', '2025-2026', 2),
('209921123001', 'CS107', '离散数学', 64, '周老师', '教楼103', '2025-2026', 2),
('209921123001', 'CS108', '数据库原理', 48, '李老师', '教楼201', '2025-2026', 2),
('209921123001', 'CS109', 'Java开发', 64, '吴老师', '机房304', '2025-2026', 2),
('209921123001', 'CS110', '人工智能', 32, '郑老师', '教楼602', '2025-2026', 2),
('209921123002', 'CS102', '数据结构', 64, '张老师', '教楼405', '2025-2026', 2),
('209921123002', 'CS111', 'Linux运维', 48, '黄老师', '机房105', '2025-2026', 2),
('209921123002', 'CS112', '信息安全', 48, '林老师', '教楼204', '2025-2026', 2),
('209921123002', 'CS113', '计算机组成', 64, '谢老师', '机房401', '2025-2026', 2),
('209921123003', 'CS103', '计算机网络', 48, '王老师', '教楼302', '2025-2026', 2),
('209921123003', 'CS114', '大数据基础', 32, '马老师', '教楼503', '2025-2026', 2),
('209921123003', 'CS115', '前端框架', 48, '孙老师', '机房301', '2025-2026', 2),
('209921123003', 'CS116', '软件测试', 32, '胡老师', '教楼205', '2025-2026', 2),
('209921123004', 'CS104', '操作系统', 64, '刘老师', '机房202', '2025-2026', 2),
('209921123004', 'CS117', '云计算', 48, '江老师', '机房502', '2025-2026', 2),
('209921123004', 'CS118', '多媒体技术', 32, '方老师', '教楼305', '2025-2026', 2),
('209921123005', 'CS105', '软件工程', 32, '陈老师', '教楼501', '2025-2026', 2),
('209921123005', 'CS119', '移动开发', 64, '钟老师', '机房404', '2025-2026', 2),
('209921123005', 'CS120', '区块链入门', 32, '姚老师', '教楼601', '2025-2026', 2),
('209921123006', 'CS106', '编译原理', 48, '赵老师', '机房403', '2025-2026', 2),
('209921123006', 'CS121', '数字逻辑', 64, '温老师', '教楼102', '2025-2026', 2),
('209921123006', 'CS122', '图像处理', 48, '涂老师', '机房203', '2025-2026', 2),
('209921123007', 'CS108', '数据库原理', 48, '李老师', '教楼201', '2025-2026', 2),
('209921123007', 'CS123', '爬虫开发', 32, '骆老师', '机房303', '2025-2026', 2);
三、项目基础配置文件
1 web.xml(路径:WebContent/WEB-INF/web.xml)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd"version="3.0"><!-- 全局编码过滤器,解决中文乱码 --><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- SpringMVC前端控制器DispatcherServlet --><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 指定SpringMVC配置文件路径 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc-servlet.xml</param-value></init-param><!-- 服务器启动时加载Servlet --><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 页面默认跳转(可选) --><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list>
</web-app>
2 springmvc-servlet.xml(src/main/resources)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!-- 扫描Controller包 --><context:component-scan base-package="com.controller"/><!-- MVC注解驱动 --><mvc:annotation-driven/><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/"/><property name="suffix" value=".jsp"/></bean><!-- 放行静态资源 --><mvc:default-servlet-handler/>
</beans>
3 hibernate.cfg.xml(src/main/resources,密码Lxq060307)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration><session-factory><!-- 适配5.x驱动,移除时区参数 --><property name="connection.url">jdbc:mysql://localhost:3306/student_course_db?useUnicode=true&characterEncoding=utf8</property><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="connection.username">root</property><property name="connection.password">Lxq060307</property><!-- 低版本Hibernate通用方言 --><property name="dialect">org.hibernate.dialect.MySQLDialect</property><!-- 控制台打印SQL --><property name="show_sql">true</property><!-- 实体映射文件 --><mapping resource="com/entity/CourseSelection.hbm.xml"/></session-factory>
</hibernate-configuration>
四、Hibernate持久层代码
1 实体类 CourseSelection.java(com.entity)
package com.entity;public class CourseSelection {private int id;private String studentId;private String courseId;private String courseName;private int courseHours;private String teacherName;private String location;private String academicYear;private int semester;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getStudentId() {return studentId;}public void setStudentId(String studentId) {this.studentId = studentId;}public String getCourseId() {return courseId;}public void setCourseId(String courseId) {this.courseId = courseId;}public String getCourseName() {return courseName;}public void setCourseName(String courseName) {this.courseName = courseName;}public int getCourseHours() {return courseHours;}public void setCourseHours(int courseHours) {this.courseHours = courseHours;}public String getTeacherName() {return teacherName;}public void setTeacherName(String teacherName) {this.teacherName = teacherName;}public String getLocation() {return location;}public void setLocation(String location) {this.location = location;}public String getAcademicYear() {return academicYear;}public void setAcademicYear(String academicYear) {this.academicYear = academicYear;}public int getSemester() {return semester;}public void setSemester(int semester) {this.semester = semester;}
}
2 CourseSelection.hbm.xml(同entity包)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping><class name="com.entity.CourseSelection" table="course_selection"><id name="id" column="id"><generator class="native"/></id><property name="studentId" column="student_id"/><property name="courseId" column="course_id"/><property name="courseName" column="course_name"/><property name="courseHours" column="course_hours"/><property name="teacherName" column="teacher_name"/><property name="location" column="location"/><property name="academicYear" column="academic_year"/><property name="semester" column="semester"/></class>
</hibernate-mapping>
3 HibernateUtil.java(com.util)
package com.util;import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;public class HibernateUtil {
private static SessionFactory sessionFactory;static {
try {
// 加载hibernate.cfg.xml构建工厂
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
五、三层业务代码
1 DAO层 CourseDao.java(com.dao)
package com.dao;import com.entity.CourseSelection;
import com.util.HibernateUtil;
import org.hibernate.Query;
import org.hibernate.Session;import java.util.List;public class CourseDao {
// 分页查询数据
public List<CourseSelection> getCourseByPage(int offset, int pageSize) {
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("FROM CourseSelection");
query.setFirstResult(offset);
query.setMaxResults(pageSize);
List<CourseSelection> list = query.list();
session.close();
return list;
}// 查询总记录数
public int getTotalCount() {
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("SELECT COUNT(*) FROM CourseSelection");
Long count = (Long) query.uniqueResult();
session.close();
return count.intValue();
}
}
2 Service层 CourseService.java(com.service)
package com.service;import com.dao.CourseDao;
import com.entity.CourseSelection;import java.util.HashMap;
import java.util.List;
import java.util.Map;public class CourseService {
private CourseDao courseDao = new CourseDao();
// 每页固定6条
private static final int PAGE_SIZE = 6;public Map<String, Object> getCoursesByPage(int currentPage) {
// 总条数
int totalCount = courseDao.getTotalCount();
// 总页数向上取整
int totalPages = (int) Math.ceil((double) totalCount / PAGE_SIZE);
// 分页偏移量
int offset = (currentPage - 1) * PAGE_SIZE;
List<CourseSelection> courseList = courseDao.getCourseByPage(offset, PAGE_SIZE);Map<String, Object> map = new HashMap<>();
map.put("courseList", courseList);
map.put("currentPage", currentPage);
map.put("totalPages", totalPages);
return map;
}
}
3 Controller层 CourseController.java(com.controller)
package com.controller;import com.service.CourseService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;import java.util.Map;@Controller
public class CourseController {
private CourseService courseService = new CourseService();// 访问路径 /list
@RequestMapping("/list")
public String list(
@RequestParam(value = "page", defaultValue = "1") int page,
Model model) {
Map<String, Object> result = courseService.getCoursesByPage(page);
model.addAttribute("courseList", result.get("courseList"));
model.addAttribute("currentPage", result.get("currentPage"));
model.addAttribute("totalPages", result.get("totalPages"));
// 视图解析器拼接:/WEB-INF/jsp/courseList.jsp
return "courseList";
}
}
六、前端分页页面 courseList.jsp(WEB-INF/jsp)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head><title>学生选课信息查询</title><style>body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 20px; }.container { width: 80%; margin: auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }table { width: 100%; border-collapse: collapse; margin-top: 20px; }th, td { padding: 10px; border: 1px solid #ddd; text-align: center; }th { background-color: #4CAF50; color: white; }tr:nth-child(even) { background-color: #f2f2f2; }.pagination { margin-top: 20px; text-align: center; }.pagination a { padding: 8px 16px; margin: 0 4px; border: 1px solid #ddd; text-decoration: none; color: #333; border-radius: 4px; }.pagination a:hover { background-color: #4CAF50; color: white; }</style>
</head>
<body>
<div class="container"><h2 align="center">学生选课信息表</h2><table><tr><th>学号</th><th>课程号</th><th>课程名</th><th>学时数</th><th>任课教师名</th><th>授课地点</th><th>学年</th><th>学期</th></tr><c:forEach items="${courseList}" var="course"><tr><td>${course.studentId}</td><td>${course.courseId}</td><td>${course.courseName}</td><td>${course.courseHours}</td><td>${course.teacherName}</td><td>${course.location}</td><td>${course.academicYear}</td><td>${course.semester}</td></tr></c:forEach></table><div class="pagination"><c:if test="${currentPage > 1}"><a href="/StudentCourseSystem/list?page=${currentPage - 1}">上一页</a></c:if><span>第 ${currentPage} 页 / 共 ${totalPages} 页</span><c:if test="${currentPage < totalPages}"><a href="/StudentCourseSystem/list?page=${currentPage + 1}">下一页</a></c:if></div>
</div>
</body>
</html>
七、部署运行测试
- 将所有依赖Jar复制到
WEB-INF/lib,右键添加到构建路径; - 项目添加至Tomcat9,清理项目与服务器缓存;
- 启动Tom,浏览器访问:
http://localhost:8080/StudentCourseSystem/list - 测试效果:每页6条、首尾页隐藏对应按钮、分页切换正常、中文无乱码。
八、常见报错调试记录
classpath resource [springmvc-servlet.xml] 不存在
原因:resources未标记源码目录;解决:右键resources → Use as Source Folder,清理重启。Dialect class not found: MySQL8Dialect
原因:Hibernate版本低,替换方言为MySQLDialect。No suitable driver
缺失mysql驱动jar,放入lib并添加构建路径。- Session/Query类找不到:缺少hibernate-core核心包。
- 500数据库连接异常:核对MySQL服务、账号密码、库名。
九、实验小结
- 整体开发流程:MySQL建表 → Eclipse Web项目搭建 → 框架XML配置 → Hibernate ORM映射 → DAO/Service分层 → SpringMVC控制器 → JSTL分页页面;
- SpringMVC核心:DispatcherServlet统一接收请求,组件扫描识别@Controller,视图解析器自动拼接页面路径;
- Hibernate分页原理:通过
setFirstResult偏移量、setMaxResults每页条数实现分页,无需手写Limit; - 分层开发优势:数据操作、业务计算、请求控制完全解耦,后期修改功能只改动对应层;
- 分页优化价值:一次性加载全部数据会占用大量内存,分页降低数据库查询压力、提升前端加载速度;
- 调试收获:掌握类路径、XML命名空间、Jar依赖、数据库连接四大类典型错误排查方法。
十、参考资料
- 《Web编程技术》余元辉,清华大学出版社,2024.8
- 《JSP设计》(第三版),Hans Bergsten当前文件内容过长,豆包只阅读了前 59%。