基于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.csslayout.csstable.csspagination.js等篇幅所限,暂不展开,完整代码可在项目源码中查看。)

五、部署与调试过程

5.1 本地Tomcat部署

  1. 在Eclipse中导入Maven项目,配置Tomcat 9.0运行时。
  2. 右键项目 → Run As → Run on Server,选择Tomcat 9.0启动。
  3. 访问 http://localhost:8080/task11/course/list 验证分页功能。

5.2 云端部署到阿里云ECS

  1. 打包WAR:在Eclipse中导出为task11.war
  2. 上传文件:使用WinSCP(SFTP)或远程桌面,将WAR包复制到服务器Tomcat的webapps目录。
  3. 修改数据库连接:确保hibernate.cfg.xmlStudentCourseController中的JDBC URL指向ECS的公网IP(或内网IP),并开放数据库端口1433。
  4. 启动Tomcat:执行startup.bat,观察日志无异常。
  5. 公网访问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条记录,包含学号、课程名、教师等字段。

图片1


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

图片2


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

图片3

六、实验小结

本次实验基于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
posted @ 2026-06-13 02:45  fangzhiyuan  阅读(9)  评论(0)    收藏  举报