news 2026/9/12 14:39:38

Java校园卡系统实战:Eclipse+Tomcat+JDBC完整开发指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java校园卡系统实战:Eclipse+Tomcat+JDBC完整开发指南

简介:这是一份基于Java开发的轻量级校园卡管理系统源码包,面向Java初学者与课程设计学生,聚焦校园场景下的饭卡充值、消费记录与账户管理等核心功能,适合作为Java SE综合实践项目或毕业设计参考。资源共30个文件,含7个Java源文件(含主程序与数据库操作类)、8个编译后class文件、10张界面截图(展示登录、充值、消费等关键页面),以及Eclipse工程配置文件(.project、.classpath)和说明文档,整体压缩包仅703KB,结构清晰,开箱即用。已有1845人学习下载,提供完整可运行的桌面应用方案:包含Swing图形界面实现、MySQL数据库连接示例(JDBC)、基础事务处理逻辑及MD5密码加密模块,代码注释充分,目录组织规范,便于理解MVC分层思想与Java桌面应用开发全流程。

1. 用 Java + Eclipse 搭建轻量级校园卡管理系统:不是 Demo,是能跑通充值、消费、挂失的真实业务闭环

你手头有一份标着“Java 校园卡管理系统”的代码,打开发现是 Eclipse 工程结构、带srcWebContent目录、依赖servlet-api.jar、数据库用的是 HSQLDB 或 MySQL —— 这不是教学玩具,而是典型的学生一卡通系统最小可行原型(MVP)。它不依赖 Spring Boot 自动装配,不走微服务架构,但完整覆盖了学生卡核心流程:发卡登记、余额查询、食堂消费扣款、自助机挂失、管理员后台审核。这类项目在高校信息中心、实训课程、毕业设计中高频出现,特点是业务逻辑清晰、数据模型扁平、IO 压力低、部署环境受限(常要求 Tomcat 7/8 + JDK 8)。如果你正面对一个需要快速交付、无云资源、仅靠本地 Eclipse 调试、且要经得起教师现场演示的 Java Web 项目,本篇就聚焦如何从零复现这个系统——不绕开web.xml配置,不跳过JDBCUtils手写连接池,不假设你已装好 Maven 插件。所有操作基于 Eclipse Oxygen/2020-06 及 JDK 1.8 环境验证,命令和路径全部可复制粘贴。


2. 用 Eclipse 创建标准 Java Web 工程并配置 Tomcat 运行时环境

2.1 新建 Dynamic Web Project 并指定兼容性版本

Eclipse 中新建项目必须严格匹配运行容器能力。校园卡系统普遍部署在老旧服务器上,Tomcat 版本多为 7.0.x 或 8.5.x,对应 Servlet 规范为 3.0 或 3.1。因此不能选择 “Dynamic Web Module Version” 为 4.0+,否则web.xml<url-pattern>写法或注解扫描会失败。

提示:若新建时未勾选 “Generate web.xml deployment descriptor”,后续无法通过右键项目 →Java EE ToolsGenerate Deployment Descriptor补全,必须手动创建WebContent/WEB-INF/web.xml文件并确保其根节点为<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">

2.1.1 具体操作步骤
  1. File → New → Dynamic Web Project
  2. Project name 输入campus-card-system(避免中文和空格)
  3. Target runtime 选择已配置的 Tomcat v7.0 Server(若未配置,先点击New Runtime→ 下载并指向 Tomcat 解压目录)
  4. Dynamic web module version 选3.0(关键!Spring Boot 项目常用 4.0,但本系统需兼容老 Tomcat)
  5. Configuration 选Default Configuration for Apache Tomcat v7.0
  6. Finish 后检查WebContent/WEB-INF/web.xml是否自动生成;若缺失,手动创建并粘贴标准头声明

2.2 添加 JDBC 驱动与 Servlet API 依赖

校园卡系统数据库通常为 MySQL 5.7 或 HSQLDB(嵌入式,免安装),Eclipse 中不使用 Maven 时,依赖需手动放入WebContent/WEB-INF/lib/

  • MySQL 驱动:下载mysql-connector-java-5.1.47.jar(兼容 JDK 1.8,新版 8.x 需改驱动类名为com.mysql.cj.jdbc.Driver
  • Servlet API:Tomcat 自带servlet-api.jar不可重复添加,否则启动报java.lang.ClassCastException: org.apache.catalina.connector.RequestFacade cannot be cast to javax.servlet.http.HttpServletRequest。正确做法是:右键项目 →Properties → Java Build Path → Libraries → Add Library → Server Runtime → Apache Tomcat v7.0
2.2.1 验证依赖是否生效

src/cn/edu/campus/dao/BaseDao.java中写测试代码:

package cn.edu.campus.dao; import java.sql.Connection; import java.sql.DriverManager; public class BaseDao { public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); // 注意:5.1.x 版本用此驱动类名 Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/campus_card?useUnicode=true&characterEncoding=UTF-8", "root", "123456"); System.out.println("JDBC 连接成功:" + conn.isValid(1)); } }

注意:运行此测试类前,必须右键 →Run As → Java Application,而非Run on Server。若报ClassNotFoundException,说明mysql-connector-java-5.1.47.jar未正确加入 Build Path:右键 JAR 文件 →Build Path → Add to Build Path

2.3 配置 Tomcat 启动参数与上下文路径

默认 Eclipse 启动 Tomcat 时,项目访问路径为http://localhost:8080/campus-card-system/,但校园卡自助终端常硬编码/card/路径。需修改部署描述符:
右键项目 →Properties → Web Deployment Assembly→ 选中srcWebContent→ Edit → 将 Deploy Path 改为/(使根路径直接映射项目);同时在Servers视图中双击 Tomcat 服务器 →Modules标签页 → 选中项目 → Edit → Context root 改为/card
重启后访问http://localhost:8080/card/login.jsp即可进入登录页。


3. 实现校园卡核心业务逻辑:发卡、消费、挂失的三层架构落地

3.1 数据库表设计与初始化脚本

校园卡系统数据模型极简,核心三张表即可支撑全流程:

  • student(学生基本信息):id(PK),name,class_id,phone
  • card(卡片主表):card_id(PK),student_id(FK),balance(DECIMAL),status(ENUM: 'normal','lost','frozen')
  • transaction_log(交易流水):id(PK),card_id,type(ENUM: 'recharge','consume','loss_report'),amount,create_time
3.1.1 MySQL 初始化 SQL(执行前确保数据库campus_card已存在)
CREATE DATABASE IF NOT EXISTS campus_card CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE campus_card; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL, class_id VARCHAR(20), phone VARCHAR(11) ); CREATE TABLE card ( card_id VARCHAR(20) PRIMARY KEY, student_id INT NOT NULL, balance DECIMAL(10,2) DEFAULT 0.00, status ENUM('normal','lost','frozen') DEFAULT 'normal', FOREIGN KEY (student_id) REFERENCES student(id) ); CREATE TABLE transaction_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, card_id VARCHAR(20) NOT NULL, type ENUM('recharge','consume','loss_report') NOT NULL, amount DECIMAL(10,2) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );

提示card_id设为VARCHAR(20)是因实际校园卡号常含字母前缀(如CARD20230001),避免INT类型溢出或补零问题;status使用ENUM而非TINYINT,便于 SQL 查询语义清晰,如WHERE status='lost'WHERE status=2更易维护。

3.2 DAO 层:手写 JDBC 连接池与事务控制

不依赖 HikariCP 或 Druid,用BasicDataSource(Apache Commons DBCP)实现轻量连接池。在src/cn/edu/campus/utils/JDBCUtils.java中:

package cn.edu.campus.utils; import org.apache.commons.dbcp.BasicDataSource; public class JDBCUtils { private static BasicDataSource dataSource; static { dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/campus_card?useUnicode=true&characterEncoding=UTF-8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setInitialSize(5); // 初始连接数 dataSource.setMaxActive(20); // 最大活跃连接 dataSource.setMaxIdle(10); // 最大空闲连接 dataSource.setMinIdle(5); // 最小空闲连接 dataSource.setRemoveAbandonedOnBorrow(true); dataSource.setRemoveAbandonedOnMaintenance(true); dataSource.setRemoveAbandonedTimeout(60); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { if (rs != null) try { rs.close(); } catch (SQLException e) {} if (ps != null) try { ps.close(); } catch (SQLException e) {} if (conn != null) try { conn.close(); } catch (SQLException e) {} } }

参数说明setRemoveAbandonedOnBorrow启用连接泄露检测,当借出连接超时未归还(removeAbandonedTimeout=60秒),自动回收该连接并打印警告日志,防止 Tomcat 启动后因连接未关闭导致Cannot create PoolableConnectionFactory错误。

3.3 Service 层:消费扣款的原子性保障

食堂消费需保证“余额足够 → 扣款 → 记录流水”三步不可分割。在src/cn/edu/campus/service/CardService.java中:

package cn.edu.campus.service; import cn.edu.campus.dao.CardDao; import cn.edu.campus.entity.Card; import cn.edu.campus.utils.JDBCUtils; import java.sql.Connection; import java.sql.SQLException; public class CardService { private CardDao cardDao = new CardDao(); public boolean consume(String cardId, double amount) { Connection conn = null; try { conn = JDBCUtils.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 查询当前余额 Card card = cardDao.findByCardId(cardId); if (card == null || card.getStatus().equals("lost") || card.getStatus().equals("frozen")) { throw new RuntimeException("卡片状态异常,无法消费"); } if (card.getBalance() < amount) { throw new RuntimeException("余额不足"); } // 2. 扣款 cardDao.updateBalance(cardId, -amount); // 3. 记录流水 cardDao.insertTransaction(cardId, "consume", amount); conn.commit(); // 提交事务 return true; } catch (Exception e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) {} } e.printStackTrace(); return false; } finally { JDBCUtils.close(conn, null, null); } } }

关键点conn.setAutoCommit(false)必须在获取连接后立即设置,否则后续executeUpdate()默认自动提交,事务失效;rollback()catch块中显式调用,确保异常时余额和流水状态一致。


4. JSP + Servlet 实现前后端交互:登录、消费、挂失页面链路

4.1 登录 Servlet:验证学号密码并跳转到学生主页

src/cn/edu/campus/servlet/LoginServlet.java处理表单 POST:

package cn.edu.campus.servlet; import cn.edu.campus.dao.StudentDao; import cn.edu.campus.entity.Student; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class LoginServlet extends HttpServlet { private StudentDao studentDao = new StudentDao(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String studentId = req.getParameter("studentId"); String password = req.getParameter("password"); // 简单校验:生产环境应加密存储密码(BCrypt) Student student = studentDao.findByStudentId(studentId); if (student != null && password.equals(student.getPassword())) { HttpSession session = req.getSession(); session.setAttribute("student", student); resp.sendRedirect(req.getContextPath() + "/student/home.jsp"); } else { req.setAttribute("error", "学号或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); } } }

注意req.getContextPath()返回/card,因此重定向路径为/card/student/home.jsp,与 Tomcat Context root 保持一致;session.setAttribute("student", student)将学生对象存入 Session,供后续 JSP 页面通过${sessionScope.student.name}获取。

4.2 消费页面:表单提交到 ConsumeServlet

WebContent/student/consume.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>校园卡消费</title></head> <body> <h2>消费金额(元)</h2> <form action="${pageContext.request.contextPath}/ConsumeServlet" method="post"> <input type="hidden" name="cardId" value="${sessionScope.student.cardId}"> <input type="number" name="amount" step="0.01" min="0.01" required> <button type="submit">确认消费</button> </form> <% if (request.getAttribute("result") != null) { %> <p style="color:red">${request.getAttribute("result")}</p> <% } %> </body> </html>

对应ConsumeServlet

// ... 导入声明 public class ConsumeServlet extends HttpServlet { private CardService cardService = new CardService(); @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String cardId = req.getParameter("cardId"); double amount = Double.parseDouble(req.getParameter("amount")); if (cardService.consume(cardId, amount)) { req.setAttribute("result", "消费成功,当前余额:" + getBalance(cardId)); } else { req.setAttribute("result", "消费失败,请重试"); } req.getRequestDispatcher("/student/consume.jsp").forward(req, resp); } private String getBalance(String cardId) { // 此处应调用 CardDao 查询,为简洁省略 return "99.50"; } }

参数说明step="0.01"确保输入框支持小数点后两位;min="0.01"防止输入 0 元;隐藏域cardId从 Session 中取出,避免前端篡改卡号。

4.3 挂失功能:状态更新与通知记录

挂失操作需更新card.status并插入transaction_log

// CardDao.java 中新增方法 public void reportLoss(String cardId) throws SQLException { String sql = "UPDATE card SET status = 'lost' WHERE card_id = ?"; update(sql, cardId); sql = "INSERT INTO transaction_log(card_id, type, amount) VALUES (?, 'loss_report', 0)"; update(sql, cardId); }

LossReportServlet调用此方法,并重定向至提示页:

// LossReportServlet.java @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String cardId = req.getParameter("cardId"); try { cardDao.reportLoss(cardId); req.setAttribute("message", "挂失成功,卡片已冻结"); } catch (Exception e) { req.setAttribute("message", "挂失失败:" + e.getMessage()); } req.getRequestDispatcher("/student/loss_result.jsp").forward(req, resp); }

5. 排查 Eclipse 常见运行时错误:从 “找不到主类” 到 JSP 编译失败

5.1 “找不到或无法加载主类 org.apache.catalina.startup.Bootstrap”

此错误绝非项目代码问题,而是 Tomcat 启动入口类路径缺失。根本原因是 Eclipse 的 Server Runtime 配置未正确关联 Tomcat 的bin/bootstrap.jarbin/tomcat-juli.jar

5.1.1 修复步骤
  1. 关闭 Eclipse
  2. 删除工作空间下.metadata/.plugins/org.eclipse.wst.server.core/tmp0/(tmp0 对应 Tomcat 实例编号)
  3. 打开Window → Preferences → Server → Runtime Environments
  4. 选中 Apache Tomcat v7.0 → Edit →Next→ 确保 “JRE” 选择 JDK 1.8(非 JRE)→Finish
  5. 右键 Servers 视图中的 Tomcat →Clean...→ 勾选Clean all projects
  6. 重新启动 Tomcat

提示:若仍报错,检查 Tomcat 安装目录bin/下是否存在bootstrap.jar;某些精简版 Tomcat 包可能缺失该文件,需从官网下载完整 zip 包替换。

5.2 JSP 编译失败:“Unable to compile class for JSP”

Eclipse 默认将 JSP 编译为 Java 文件存于work/Catalina/localhost/[context]/,若该目录权限不足或磁盘满,编译失败。

5.2.1 定位与清理
  • 查看 Tomcat 控制台输出,找到类似org.apache.jasper.JasperException: Unable to compile class for JSP的堆栈
  • 进入Servers/Tomcat v7.0 Server at localhost-config/→ 打开server.xml→ 找到<Engine name="Catalina" defaultHost="localhost">下的<Host>标签,确认appBase值(默认webapps
  • 清理编译缓存:删除workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/work/Catalina/localhost/card/全部内容
  • 重启 Tomcat,首次访问 JSP 时会重新编译

5.3 中文乱码终极解决方案:三处强制 UTF-8

校园卡系统涉及姓名、班级等中文字段,乱码常出现在三处:

位置配置方式说明
JSP 页面<%@ page contentType="text/html;charset=UTF-8" %>必须放在每页首行,且charset=UTF-8不可省略
Tomcat 请求编码Servers/Tomcat v7.0 → Overview → Open launch configuration → Arguments → VM arguments添加-Dfile.encoding=UTF-8影响request.getParameter()解码
MySQL 连接 URLjdbc:mysql://localhost:3306/campus_card?useUnicode=true&characterEncoding=UTF-8useUnicode=true启用 Unicode,characterEncoding=UTF-8指定编码

验证方法:在LoginServlet中打印System.out.println("学号:" + studentId);,若控制台显示乱码,则 VM 参数未生效;若浏览器显示乱码而控制台正常,则 JSP 页面未声明 charset。

5.4 数据库连接超时:调整 DBCP 连接池参数

当系统空闲 8 小时后首次访问报Communications link failure,是 MySQL 默认wait_timeout=28800(8 小时)断开空闲连接所致。

5.4.1 两种修复方案

方案一(推荐):在 JDBC URL 中添加自动重连

dataSource.setUrl("jdbc:mysql://localhost:3306/campus_card?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=3");

方案二:修改 MySQL 配置
编辑my.cnf(Linux)或my.ini(Windows),在[mysqld]下添加:

wait_timeout = 288000 # 80 小时 interactive_timeout = 288000

然后重启 MySQL 服务。

参数说明autoReconnect=true启用自动重连,failOverReadOnly=false避免重连后连接变为只读,maxReconnects=3限制重试次数防死循环。


6. 优化校园卡系统的响应速度:JSP 缓存、SQL 索引与连接复用技巧

6.1 JSP 页面启用缓冲与静态资源分离

Eclipse 默认 JSP 编译不启用缓冲,导致每次请求都重新解析。在web.xml中添加:

<jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <buffer>8kb</buffer> <trim-blank-lines>true</trim-blank-lines> </jsp-property-group> </jsp-config>

同时,将 CSS/JS/图片移出WebContent/,放入WebContent/static/目录,并在 JSP 中引用:

<link rel="stylesheet" href="${pageContext.request.contextPath}/static/css/main.css"> <script src="${pageContext.request.contextPath}/static/js/util.js"></script>

效果buffer减少输出流 flush 次数;trim-blank-lines删除 JSP 中多余空行,减小响应体积;静态资源分离后,浏览器可长期缓存,降低 Tomcat IO 压力。

6.2 为高频查询字段添加数据库索引

校园卡系统最慢操作通常是按卡号查余额、按学号查卡片。在 MySQL 中执行:

-- 为 card 表 card_id 字段加唯一索引(主键已存在,此步可省略) ALTER TABLE card ADD UNIQUE INDEX idx_card_id (card_id); -- 为 student 表 student_id 加索引(假设 student.id 是主键,但 card.student_id 未索引) ALTER TABLE card ADD INDEX idx_student_id (student_id); -- 为 transaction_log 表 card_id 和 create_time 加联合索引(查某卡最近 10 笔流水) ALTER TABLE transaction_log ADD INDEX idx_card_time (card_id, create_time DESC);

验证索引生效:执行EXPLAIN SELECT * FROM transaction_log WHERE card_id='CARD20230001' ORDER BY create_time DESC LIMIT 10;,若typerefkey显示idx_card_time,则索引命中。

6.3 复用 PreparedStatement 避免 SQL 解析开销

CardDao.java中,将updateBalance方法从字符串拼接改为预编译:

// 旧写法(每次执行都解析 SQL) String sql = "UPDATE card SET balance = balance - " + amount + " WHERE card_id = '" + cardId + "'"; // 新写法(PreparedStatement 复用执行计划) private static final String SQL_UPDATE_BALANCE = "UPDATE card SET balance = balance + ? WHERE card_id = ?"; // ... ps = conn.prepareStatement(SQL_UPDATE_BALANCE); ps.setDouble(1, -amount); // 扣款传负数 ps.setString(2, cardId); ps.executeUpdate();

性能对比:对同一 SQL 模板,PreparedStatement 在数据库端只编译一次执行计划,后续调用直接绑定参数执行,比 Statement 快 3~5 倍;且彻底杜绝 SQL 注入风险。

6.4 使用 Eclipse MAT 分析内存泄漏:定位未关闭的 ResultSet

当 Tomcat 运行数小时后OutOfMemoryError: GC overhead limit exceeded,大概率是 DAO 层未关闭ResultSet。用 Eclipse Memory Analyzer Tool(MAT)分析 heap dump:

  1. Servers视图右键 Tomcat →Debug(非 Run)
  2. 访问几次消费页面后,右键 Tomcat →Heap Dump
  3. 打开生成的heap_dump.hprofLeak Suspects Report
  4. 若发现大量com.mysql.jdbc.JDBC4ResultSet实例,说明ResultSet.close()未调用
  5. 修改JDBCUtils.close()方法,确保rs.close()在 finally 块中执行

关键技巧:在close()方法开头添加日志System.out.println("Closing ResultSet: " + rs);,若日志不输出,证明rs为 null 或未创建,需检查 DAO 中executeQuery()后是否遗漏赋值。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 14:37:28

MQTT公网安全实战:从1883明文到8883 TLS加密,用Let‘s Encrypt保护设备通信

一篇一篇写到第九篇&#xff0c;MQTT 这条线已经从“能连上”走到了“能在公网稳定跑”。前面几篇我们把 EMQX 服务端搭了起来&#xff0c;也把客户端的连接认证、Topic 设计捋了一遍&#xff0c;但一直留着一个隐患——服务端和客户端之间基本都在用 1883 端口直连&#xff0c…

作者头像 李华
网站建设 2026/9/12 14:36:24

A*算法与非线性优化融合的智能路径规划技术

1. 项目概述&#xff1a;A*与非线性优化的融合路径规划 在机器人导航、游戏AI和物流调度等领域&#xff0c;路径规划始终是核心挑战。传统A 算法虽然能保证找到最短路径&#xff0c;但在复杂环境中存在计算效率低、路径不够平滑等问题。而单纯的非线性优化方法又难以处理大规模…

作者头像 李华
网站建设 2026/9/12 14:30:34

龙珠超109集战斗艺术与角色成长解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 14:30:20

Kafka Consumer 如何从 classic 协议在线迁移到 group.protocol=consumer

Kafka Consumer 如何从 classic 协议在线迁移到 group.protocolconsumer 【免费下载链接】Kafka Apache Kafka - A distributed event streaming platform 项目地址: https://gitcode.com/GitHub_Trending/kafka4/kafka 如果你的消费组目前运行在 Kafka 4.0 集群上&…

作者头像 李华