基于SpringMVC+Hibernate的学生选课分页查询系统(附完整代码)
基于SpringMVC+Hibernate的学生选课分页查询系统(附完整代码)
本项目实现了一个具有玻璃拟态风格的学生选课查询系统,支持分页显示、动态粒子背景、鼠标吸引及点击涟漪效果,并成功部署到阿里云ECS。系统基于SpringMVC + Hibernate + SQL Server,从数据库分页读取选课记录(每页6条),提供首页、上一页、下一页、尾页等分页控件。本文将详细介绍开发过程、核心代码以及部署调试经验。
在线演示(实验期间有效):http://118.31.44.31:8080/task11/course/list
一、实验目的
掌握数据库分页显示技术在JSP页面设计中的应用;理解SpringMVC + Hibernate整合流程,实现基于ORM框架的分页查询。
二、实验内容与设计思想
建立MySQL数据库(实际使用SQL Server),创建学生选课信息表(学号、课程号、课程名、学时数、任课教师名、授课地点、学年、学期),要求至少30条记录。基于SpringMVC+Hibernate框架开发学生信息查询系统,实现分页查询显示选课记录(每页6条记录)。UI设计采用玻璃拟态风格,搭配动态粒子背景、鼠标吸引连线、点击涟漪等视觉特效。
设计思想:严格遵循MVC分层模式,将实体层(Bean)、数据访问层(DAO)、业务逻辑层(Service)和控制器层(Controller)分离。使用Hibernate作为ORM框架(同时提供原生SQL实现以绕过缓存),SpringMVC处理请求分发与视图渲染。前端采用JSP + JSTL动态生成表格及分页链接,CSS实现毛玻璃面板和响应式布局,Canvas实现全屏粒子动画。
三、实验环境
- 操作系统:Microsoft Windows 10/11
- 编程环境:JDK 1.8、Tomcat 9.0、Eclipse IDE for JavaEE
- 数据库:SQL Server 2019
- 依赖管理:Maven
- 部署环境:阿里云ECS(Windows Server)
四、项目结构与核心代码
📁 项目目录结构(Maven风格,点击展开)
task11/
├── pom.xml
├── src/main/java
│ └── com/task11
│ ├── bean/
│ │ ├── PageResult.java
│ │ └── StudentCourse.java
│ ├── controller/
│ │ └── StudentCourseController.java
│ ├── dao/
│ │ └── StudentCourseDao.java
│ ├── service/
│ │ ├── StudentCourseService.java
│ │ └── impl/StudentCourseServiceImpl.java
│ └── util/
│ └── HibernateUtil.java
├── src/main/resources
│ └── hibernate.cfg.xml
└── src/main/webapp
├── WEB-INF/
│ ├── jsp/studentCourse.jsp
│ ├── web.xml
│ └── springmvc-servlet.xml
├── css/
│ ├── background.css
│ ├── glass.css
│ ├── layout.css
│ ├── table.css
│ └── query.css
└── js/
├── particle.js
├── ring.js
├── resize.js
├── pagination.js
└── query.js
4.1 Maven配置(pom.xml)
📄 pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.task11</groupId>
<artifactId>task11</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.3.29</spring.version>
<hibernate.version>5.6.15.Final</hibernate.version>
</properties>
<dependencies>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Hibernate Core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Hibernate JPA API -->
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<!-- SQL Server JDBC Driver -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>12.4.2.jre8</version>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- JSP API -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.36</version>
</dependency>
</dependencies>
<build>
<finalName>task11</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.2 Bean层
📄 StudentCourse.java(实体类)
package com.task11.bean;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "student_course")
public class StudentCourse implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "student_id")
private String studentId;
@Column(name = "course_id")
private String courseId;
@Column(name = "course_name")
private String courseName;
@Column(name = "credit_hours")
private int creditHours;
@Column(name = "teacher_name")
private String teacherName;
@Column(name = "location")
private String location;
@Column(name = "academic_year")
private String academicYear;
@Column(name = "semester")
private String semester;
// 无参构造器、getter/setter(省略,请自行补充)
}
📄 PageResult.java(分页结果封装)
package com.task11.bean;
import java.io.Serializable;
import java.util.List;
public class PageResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
private List<T> list;
private long total;
public PageResult() {}
public PageResult(List<T> list, long total) {
this.list = list;
this.total = total;
}
// getter/setter
}
4.3 数据访问层(DAO)
📄 StudentCourseDao.java(分页查询 + 原生SQL)
package com.task11.dao;
import com.task11.bean.StudentCourse;
import com.task11.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import java.util.List;
public class StudentCourseDao {
private final SessionFactory sessionFactory;
public StudentCourseDao() {
this.sessionFactory = HibernateUtil.getSessionFactory();
}
// 原生SQL分页(SQL Server语法)
public List<StudentCourse> findByPageNative(int pageNo, int pageSize) {
try (Session session = sessionFactory.openSession()) {
String sql = "SELECT * FROM student_course ORDER BY student_id OFFSET :offset ROWS FETCH NEXT :pageSize ROWS ONLY";
Query<StudentCourse> query = session.createNativeQuery(sql, StudentCourse.class);
query.setParameter("offset", (pageNo - 1) * pageSize);
query.setParameter("pageSize", pageSize);
return query.list();
}
}
public long countAllNative() {
try (Session session = sessionFactory.openSession()) {
Query<Long> query = session.createNativeQuery("SELECT COUNT(*) FROM student_course", Long.class);
return query.uniqueResult();
}
}
}
4.4 业务逻辑层(Service)
📄 StudentCourseService.java & StudentCourseServiceImpl.java
// 接口
package com.task11.service;
import com.task11.bean.PageResult;
import com.task11.bean.StudentCourse;
public interface StudentCourseService {
PageResult<StudentCourse> getCoursesByPage(int pageNo, int pageSize);
}
// 实现类
package com.task11.service.impl;
import com.task11.bean.PageResult;
import com.task11.bean.StudentCourse;
import com.task11.dao.StudentCourseDao;
import com.task11.service.StudentCourseService;
public class StudentCourseServiceImpl implements StudentCourseService {
private final StudentCourseDao dao = new StudentCourseDao();
@Override
public PageResult<StudentCourse> getCoursesByPage(int pageNo, int pageSize) {
long total = dao.countAllNative();
java.util.List<StudentCourse> list = dao.findByPageNative(pageNo, pageSize);
return new PageResult<>(list, total);
}
}
4.5 控制器层(Controller)
📄 StudentCourseController.java(SpringMVC + JDBC直连分页)
package com.task11.controller;
import java.sql.*;
import java.util.*;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class StudentCourseController {
private static final String URL = "jdbc:sqlserver://118.31.44.31:1433;databaseName=task11;encrypt=false;trustServerCertificate=true";
private static final String USER = "sa";
private static final String PASSWORD = "YourStrongP@ssw0rd";
public static class CourseItem {
public String studentId, courseId, courseName, teacherName, location;
public int creditHours;
// getters 省略
}
@GetMapping("/course/list")
public String listCourses(@RequestParam(defaultValue = "1") int pageNo, Model model) {
int pageSize = 6;
List<CourseItem> list = new ArrayList<>();
long total = 0;
String errorMsg = null;
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD)) {
// 查询总记录数
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM student_course")) {
if (rs.next()) total = rs.getLong(1);
}
// 分页查询
String sql = "SELECT student_id, course_id, course_name, credit_hours, teacher_name, location " +
"FROM student_course ORDER BY student_id OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, (pageNo - 1) * pageSize);
ps.setInt(2, pageSize);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
CourseItem ci = new CourseItem();
ci.studentId = rs.getString("student_id");
ci.courseId = rs.getString("course_id");
ci.courseName = rs.getString("course_name");
ci.creditHours = rs.getInt("credit_hours");
ci.teacherName = rs.getString("teacher_name");
ci.location = rs.getString("location");
list.add(ci);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
errorMsg = "数据库错误: " + e.getMessage();
}
int totalPages = (int) Math.ceil((double) total / pageSize);
model.addAttribute("list", list);
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", totalPages);
if (errorMsg != null) model.addAttribute("error", errorMsg);
return "studentCourse";
}
}
4.6 Hibernate配置文件(hibernate.cfg.xml)
📄 hibernate.cfg.xml
<?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>
<property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="hibernate.connection.url">jdbc:sqlserver://118.31.44.31:1433;databaseName=task11;encrypt=false;trustServerCertificate=true</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password">YourStrongP@ssw0rd</property>
<property name="hibernate.dialect">org.hibernate.dialect.SQLServer2012Dialect</property>
<property name="show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">none</property>
<mapping class="com.task11.bean.StudentCourse"/>
</session-factory>
</hibernate-configuration>
4.7 SpringMVC配置文件(springmvc-servlet.xml)
📄 springmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.task11.controller"/>
<mvc:resources mapping="/css/**" location="/WEB-INF/css/"/>
<mvc:resources mapping="/js/**" location="/WEB-INF/js/"/>
<mvc:default-servlet-handler/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
4.8 Web部署描述文件(web.xml)
📄 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_1.xsd"
version="3.1">
<filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
4.9 JSP视图(studentCourse.jsp)
注意:博客园不会执行JSP代码,以下代码块可安全显示。
📄 studentCourse.jsp(分页表格 + 粒子背景)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>学生选课查询系统 | 玻璃粒子风格</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/background.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/glass.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/layout.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/table.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/query.css">
</head>
<body>
<canvas id="particleCanvas"></canvas>
<div class="glass-panel">
<h2>✨ 学生选课查询系统 ✨</h2>
<table class="glass-table">
<thead>
<tr><th>学号</th><th>课程号</th><th>课程名</th><th>学分</th><th>教师</th><th>地点</th></tr>
</thead>
<tbody>
<c:forEach var="course" items="${list}">
<tr>
<td>${course.studentId}</td>
<td>${course.courseId}</td>
<td>${course.courseName}</td>
<td>${course.creditHours}</td>
<td>${course.teacherName}</td>
<td>${course.location}</td>
</tr>
</c:forEach>
</tbody>
</table>
<div class="pagination">
<c:if test="${currentPage > 1}">
<a href="${pageContext.request.contextPath}/course/list?pageNo=1" class="page-btn">首页</a>
<a href="${pageContext.request.contextPath}/course/list?pageNo=${currentPage - 1}" class="page-btn">上一页</a>
</c:if>
<c:if test="${currentPage <= 1}">
<span class="page-btn disabled">首页</span>
<span class="page-btn disabled">上一页</span>
</c:if>
<span class="page-info">第 ${currentPage} 页 / 共 ${totalPages} 页</span>
<c:if test="${currentPage < totalPages}">
<a href="${pageContext.request.contextPath}/course/list?pageNo=${currentPage + 1}" class="page-btn">下一页</a>
<a href="${pageContext.request.contextPath}/course/list?pageNo=${totalPages}" class="page-btn">尾页</a>
</c:if>
<c:if test="${currentPage >= totalPages}">
<span class="page-btn disabled">下一页</span>
<span class="page-btn disabled">尾页</span>
</c:if>
</div>
</div>
<script src="${pageContext.request.contextPath}/js/particle.js"></script>
<script src="${pageContext.request.contextPath}/js/ring.js"></script>
<script src="${pageContext.request.contextPath}/js/resize.js"></script>
<script src="${pageContext.request.contextPath}/js/pagination.js"></script>
<script src="${pageContext.request.contextPath}/js/query.js"></script>
</body>
</html>
4.10 CSS样式(核心)
🎨 glass.css(毛玻璃中央面板)
.glass-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
max-width: 950px;
background: rgba(20, 20, 40, 0.45);
backdrop-filter: blur(20px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 32px;
padding: 25px 30px 35px;
box-shadow: 0 25px 45px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.1) inset;
color: #f0f0ff;
text-align: center;
z-index: 10;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.glass-panel:hover {
transform: translate(-50%, -50%) scale(1.01);
border-color: rgba(255,255,255,0.5);
}
🎨 particle.js(动态粒子背景 + 鼠标吸引 + 连线)
(function() {
function init() {
const canvas = document.getElementById('particleCanvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
let width, height;
let particles = [];
const PARTICLE_COUNT = 180;
let mouse = { x: null, y: null };
const colors = ['rgba(180, 100, 255,', 'rgba(80, 220, 255,', 'rgba(255, 200, 100,'];
function resize() {
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
particles = [];
for (let i = 0; i < PARTICLE_COUNT; i++) {
particles.push({
x: Math.random() * width,
y: Math.random() * height,
vx: (Math.random() - 0.5) * 0.8,
vy: (Math.random() - 0.5) * 0.8,
radius: Math.random() * 2.5 + 1,
color: colors[Math.floor(Math.random() * colors.length)] + '0.8)'
});
}
}
function draw() {
ctx.clearRect(0, 0, width, height);
for (let p of particles) {
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
ctx.fillStyle = p.color;
ctx.fill();
if (mouse.x !== null && mouse.y !== null) {
let dx = mouse.x - p.x, dy = mouse.y - p.y, dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 120) {
let angle = Math.atan2(dy, dx), force = (120 - dist) / 120 * 0.03;
p.vx += Math.cos(angle) * force;
p.vy += Math.sin(angle) * force;
}
}
p.x += p.vx;
p.y += p.vy;
if (p.x < 0) p.x = width;
if (p.x > width) p.x = 0;
if (p.y < 0) p.y = height;
if (p.y > height) p.y = 0;
p.vx *= 0.99;
p.vy *= 0.99;
}
// 绘制连线
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
let dx = particles[i].x - particles[j].x, dy = particles[i].y - particles[j].y, dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 100) {
ctx.beginPath();
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.strokeStyle = 'rgba(180, 180, 255, ' + (1 - dist / 100) * 0.3 + ')';
ctx.lineWidth = 0.6;
ctx.stroke();
}
}
}
requestAnimationFrame(draw);
}
window.addEventListener('resize', resize);
window.addEventListener('mousemove', e => { mouse.x = e.clientX; mouse.y = e.clientY; });
window.addEventListener('mouseleave', () => { mouse.x = null; mouse.y = null; });
resize();
draw();
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
🎨 ring.js(点击涟漪效果)
(function() {
const style = document.createElement('style');
style.textContent = `
.ripple-ring {
position: fixed;
width: 100px;
height: 100px;
margin-left: -50px;
margin-top: -50px;
border-radius: 50%;
border: 2px solid rgba(180, 160, 255, 0.7);
pointer-events: none;
z-index: 999;
animation: rippleExpand 0.8s ease-out forwards;
}
@keyframes rippleExpand {
0% { transform: scale(0); opacity: 1; }
100% { transform: scale(1.5); opacity: 0; }
}
`;
document.head.appendChild(style);
document.addEventListener('click', function(e) {
const ring = document.createElement('div');
ring.className = 'ripple-ring';
ring.style.left = e.clientX + 'px';
ring.style.top = e.clientY + 'px';
document.body.appendChild(ring);
ring.addEventListener('animationend', () => ring.remove());
});
})();
(其他CSS及JS文件如background.css、layout.css、table.css、pagination.js等篇幅所限,暂不展开,完整代码可在项目源码中查看。)
五、部署与调试过程
5.1 本地Tomcat部署
- 在Eclipse中导入Maven项目,配置Tomcat 9.0运行时。
- 右键项目 → Run As → Run on Server,选择Tomcat 9.0启动。
- 访问
http://localhost:8080/task11/course/list验证分页功能。
5.2 云端部署到阿里云ECS
- 打包WAR:在Eclipse中导出为
task11.war。 - 上传文件:使用WinSCP(SFTP)或远程桌面,将WAR包复制到服务器Tomcat的
webapps目录。 - 修改数据库连接:确保
hibernate.cfg.xml和StudentCourseController中的JDBC URL指向ECS的公网IP(或内网IP),并开放数据库端口1433。 - 启动Tomcat:执行
startup.bat,观察日志无异常。 - 公网访问:
http://<ECS公网IP>:8080/task11/course/list
5.3 典型问题与解决方案
| 问题 | 原因 | 解决方法 |
|---|---|---|
hibernate.cfg.xml not found |
配置文件未放在classpath根目录 | 移动到src/main/resources,部署后位于WEB-INF/classes |
| SQL Server TCP/IP连接失败 | 默认禁用TCP/IP | 启用SQL Server配置管理器中的TCP/IP,固定端口1433 |
| 实例名解析超时 | 使用实例名连接,但Browser服务未启动 | 改用IP+端口方式连接 |
| 分页链接404 | 缺少项目上下文路径 | JSP中使用${pageContext.request.contextPath}/course/list |
| 静态资源404 | Spring MVC资源映射路径错误 | 修改<mvc:resources>的location为/WEB-INF/css/等 |
| Hibernate查询返回旧数据 | 二级缓存未刷新 | 改用原生SQL查询,绕过缓存 |
| 粒子背景不显示 | Canvas元素未找到或JS执行过早 | 将初始化代码包裹在DOMContentLoaded中 |
| 阿里云安全组未放行8080/1433 | 默认安全组仅允许22/3389 | 添加入方向规则,允许TCP 8080和1433 |
| Tomcat端口被占用 | 其他进程占用8080 | `netstat -ano |
5.4 最终效果
- 玻璃拟态面板居中,带毛玻璃效果和悬浮缩放动画。
- 表格展示学号、课程号、课程名、学分、教师、地点共6列。
- 分页控件完整(首页、上一页、下一页、尾页),每页6条记录。
- 动态粒子背景:粒子随机运动、鼠标吸引、粒子间连线、点击产生涟漪。
- 云端部署后所有功能正常,与本地表现一致。
5.5 演示图片
以下为本地及云端测试过程中的关键界面截图,展示了分页查询的不同状态和视觉效果。
图1:数据库表结构及初始数据(部分)
student_course表中插入60条记录,包含学号、课程名、教师等字段。

图2:云端部署成功页面(第1页)
公网IP访问,玻璃面板显示第1页数据,粒子背景正常。

图3:云端分页功能验证(第2页)
点击“下一页”,URL变为?pageNo=2,表格显示第2页数据,分页控件同步更新。

六、实验小结
本次实验基于SpringMVC + Hibernate + SQL Server实现了学生选课分页查询系统,完成了从数据库设计、实体映射、DAO/Service分层、Controller编写、JSP视图美化到本地调试和云端部署的全流程。小组四人采用模块化协同开发模式,各司其职:
- 方致沅:系统架构设计,SpringMVC+Hibernate整合,解决配置文件路径、数据库连接、原生SQL分页等后端核心问题。
- 李润哲:页面视觉设计,玻璃拟态CSS、动态背景适配,解决静态资源404、Canvas穿透、响应式布局等前端问题。
- 王鑫杰:分页控件实现,粒子脚本调试,解决分页链接上下文缺失、Canvas初始化时机、分页边界处理等问题。
- 康奕彬:Tomcat部署,阿里云ECS发布与故障排查,克服端口占用、安全组配置、SQL Server网络配置等实际运维困难。
通过本次实验,我们深入掌握了SpringMVC+Hibernate的完整整合流程,理解了ORM分页查询与原生SQL的性能差异,积累了前端Canvas动画及毛玻璃效果的实践经验,同时提升了团队协作与云服务器部署的能力。这些技能将为后续课程设计及企业级开发打下坚实基础。
七、附录
- 《Web编程技术》(第二版),余元辉主编,清华大学出版社,2024.8
- 《JSP设计》(第三版),Hans Bergsten 著,林琪、朱涛江译,O‘Reilly Media,2017

浙公网安备 33010602011771号