ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java Web投票系统课程设计:Access数据库+Tomcat部署全指南

Java Web投票系统课程设计:Access数据库+Tomcat部署全指南 简介这是一份面向高校Web开发课程设计的Java Web投票系统完整实现适用于Java Web初学者进行项目实践与课设参考。系统采用JSPServletJDBC技术栈包含普通用户投票入口与管理员后台管理双模块支持用户匿名投票、管理员登录账号admin/admin及题库增删改查等核心功能。资源包共14个文件涵盖9个JSP页面如index.jsp、login.jsp、manage.jsp等构成前后端交互逻辑、1个Java类DBBean.java用于数据库连接、1个XML配置文件web.xml定义Servlet映射、1个MDB数据库文件Access格式存储投票数据及1个GIF图标资源整体仅16KB轻量易部署。已有866人学习下载提供开箱即用的源码数据库清晰目录结构可直接导入Tomcat运行是理解MVC分层思想、JSP表单处理与简单权限控制的典型教学案例。1. 这不是又一个“Hello World”投票Demo它是一套能直接跑通、改完就能交课设、部署到Tomcat上真能用的Java Web投票系统含MDB数据库完整JSP流程你手头这份web课程设计基于Java的投票系统源码数据库.zip不是那种只有一张投票页、后台硬编码几个选项、连用户登录都靠JS弹窗糊弄的“伪Web项目”。它是一套真实走通了MVC雏形、分角色权限、带数据库增删改查、有管理后台和前台展示、所有JSP页面可独立访问、DBBean封装了JDBC连接池雏形的Java Web课程设计级工程。普通用户从index.jsp进入不需登录——这是课程设计里最常被忽略但实际教学要求的“游客模式”管理员从login.jsp登录账号密码都是admin进manage.jsp管理议题、增删选项、查看统计——这才是课程设计答辩时老师会点开看的“核心功能页”。它用的是vote.mdbAccess数据库不是MySQL也不是H2这意味着你不用装服务端、不用配驱动、解压即用但同时也意味着——你得亲手把MDB文件放进Tomcat的webapps/yourapp/WEB-INF/classes/下路径错一格就报SQLException: No suitable driver。我当年帮三个班同学调试这个项目80%的翻车点都在这里。如果你正卡在“页面打不开”“登录后白屏”“投票没反应”别急着重写先确认MDB放对位置、DBBean.java里的Class.forName(sun.jdbc.odbc.JdbcOdbcDriver)是否还在用JDK 8u251已移除、web.xml里servlet mapping有没有漏掉*.jsp——这些才是课程设计落地的真实水位线。2. 从解压到启动五步走通Tomcat部署全流程含MDB路径、JDBC驱动兼容性、web.xml配置校验2.1 解压结构还原看清vote文件夹里每个文件的真实职责拿到.zip后不要直接丢进IDE。先手动解压观察目录树。核心结构必须是vote/ ├── index.jsp ← 普通用户首页显示当前可投议题、投票按钮 ├── login.jsp ← 管理员登录页表单提交到process.jsp验证 ├── manage.jsp ← 管理后台首页列出所有议题含“新增”“删除”“查看统计”链接 ├── add.jsp ← 新增议题页输入议题名、选项逗号分隔 ├── delete.jsp ← 删除议题页通过URL参数?id1传入ID ├── checkadmin.jsp ← 管理员身份校验页验证session中admin标志 ├── vote.jsp ← 投票处理页接收选项ID更新MDB中对应计数 ├── info.jsp ← 统计展示页查询各选项得票数并生成表格 ├── process.jsp ← 登录处理器验证账号密码设置session ├── back.gif ← 静态资源返回按钮图片路径必须匹配 ├── WEB-INF/ │ ├── web.xml ← 关键servlet配置、welcome-file-list、error-page │ └── classes/ │ └── DBBean.class ← 编译后的数据库操作类源码在DBBean.java └── vote.mdb ← Access数据库文件必须放在此处或classes同级提示DBBean.java是整个系统的数据中枢。它用sun.jdbc.odbc.JdbcOdbcDriver连接.mdb文件路径写死为jdbc:odbc:Driver{Microsoft Access Driver (*.mdb)};DBQdbPath。dbPath的拼接逻辑在DBBean构造函数里——它默认找this.getClass().getResource(/vote.mdb).getPath()所以vote.mdb必须放在classes/目录下否则getResource返回 null。2.2 Tomcat部署三原则路径、驱动、上下文路径缺一不可课程设计环境通常用 Tomcat 7 或 8JDK 1.7/1.8。部署不是复制粘贴那么简单路径原则将整个vote文件夹不是zip不是vote内部文件是包含index.jsp的那层文件夹复制到tomcat/webapps/下。启动后应用路径自动为http://localhost:8080/vote/。驱动原则JDK 8u251 已移除JdbcOdbcDriver。若用 JDK 8u261 或更高版本必须替换驱动下载UCanAccess纯Java Access驱动无需ODBC桥接将ucanaccess-5.0.1.jar,commons-lang3-3.8.1.jar,commons-logging-1.2.jar,hsqldb-2.5.0.jar,jackcess-3.0.1.jar全部放入vote/WEB-INF/lib/需新建此目录修改DBBean.java中的驱动加载和URL// 原代码JDK 8u251 Class.forName(sun.jdbc.odbc.JdbcOdbcDriver); String url jdbc:odbc:Driver{Microsoft Access Driver (*.mdb)};DBQ dbPath; // 替换为JDK 8u251 Class.forName(net.ucanaccess.jdbc.UcanaccessDriver); String url jdbc:ucanaccess:// dbPath; // dbPath指向vote.mdb绝对路径上下文原则web.xml中welcome-file-list必须包含index.jsp否则访问http://localhost:8080/vote/会404welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list2.3 启动验证四步法用浏览器行为反推系统状态不要等全部写完再测。每完成一步立刻验证步骤访问地址预期现象失败信号关键日志位置1. 应用部署http://localhost:8080/vote/显示首页index.jsp有“投票”按钮、“管理员入口”链接HTTP 404tomcat/logs/catalina.out查deploy日志2. 静态资源http://localhost:8080/vote/back.gif图片正常显示HTTP 404检查vote/back.gif是否在根目录3. 登录流程http://localhost:8080/vote/login.jsp→ 输入 admin/admin → 提交跳转至manage.jsp顶部显示“欢迎管理员”白屏或跳回login.jsptomcat/logs/catalina.out查process.jsp异常4. 数据库连通http://localhost:8080/vote/manage.jsp页面底部显示“当前共有X个议题”列表可展开列表为空或报错SQLExceptionDBBean.java中getConnection()方法内异常捕获注意manage.jsp里调用DBBean.getTopics()查询议题该方法会触发getConnection()。如果此处报错90%是vote.mdb路径不对或驱动未加载。打开DBBean.java在getConnection()方法开头加一行System.out.println(DB path: dbPath);重启Tomcat看控制台输出的路径是否真实存在。3. DBBean.java深度拆解为什么它既是救命稻草也是最大雷区含连接池模拟、SQL注入防护、事务边界3.1 DBBean的三层结构Connection获取、Statement执行、ResultSet封装DBBean.java表面简单实则藏着课程设计里最易被忽略的工程细节。它不是简单的“new Connection”而是做了三层封装public class DBBean { private String dbPath; // MDB文件绝对路径关键 public DBBean() { // 1. 动态获取MDB路径getClass().getResource(/vote.mdb).getPath() // 注意getResource返回的是URLgetPath()可能含空格或中文需URLDecoder.decode URL dbUrl this.getClass().getResource(/vote.mdb); if (dbUrl null) throw new RuntimeException(vote.mdb not found in classpath); dbPath URLDecoder.decode(dbUrl.getPath(), UTF-8); // 修复中文路径乱码 } public Connection getConnection() throws Exception { // 2. 加载驱动JDK8u251必须用UCanAccess Class.forName(net.ucanaccess.jdbc.UcanaccessDriver); // 3. 创建连接UCanAccess不支持DBQ参数直接传文件路径 return DriverManager.getConnection(jdbc:ucanaccess:// dbPath); } public ResultSet executeQuery(String sql) throws Exception { Connection conn getConnection(); Statement stmt conn.createStatement(); return stmt.executeQuery(sql); // 无参数化存在SQL注入风险 } }逻辑说明DBBean的设计意图是“隐藏数据库细节”但executeQuery直接拼SQLaddTopic方法里String sql INSERT INTO topics (name) VALUES ( topicName )—— 这是典型SQL注入温床。课程设计允许但你要知道如果老师问“如何防止SQL注入”答案就是把这里改成PreparedStatement。3.2 连接池的朴素实现用静态变量模拟单例Connection非线程安全但够用课程设计不考高并发但DBBean用了一个非常务实的优化避免每次查询都新建Connection。它用静态变量缓存连接private static Connection conn null; public Connection getConnection() throws Exception { if (conn null || conn.isClosed()) { conn DriverManager.getConnection(jdbc:ucanaccess:// dbPath); } return conn; }参数说明这叫“懒汉式单例Connection”好处是减少连接开销坏处是多用户同时投票时conn被共享可能因未关闭导致锁表或连接超时。课程设计单机演示没问题但若想升级必须改为ThreadLocalConnection或引入HikariCP。3.3 事务边界的隐式约定投票成功后必须commit失败必须rollbackvote.jsp是投票动作的终点。它调用DBBean.updateVote(optionId)而该方法内部执行UPDATE options SET countcount1 WHERE id?。但原代码没有显式事务控制// 原代码危险 public void updateVote(int optionId) throws Exception { Connection conn getConnection(); Statement stmt conn.createStatement(); stmt.executeUpdate(UPDATE options SET countcount1 WHERE id optionId); // 缺少 conn.commit() 和 conn.rollback() }为什么必须补Access数据库对事务敏感。若中途异常如optionId不存在count可能被错误更新且无法回滚。正确做法public void updateVote(int optionId) throws Exception { Connection conn getConnection(); conn.setAutoCommit(false); // 关闭自动提交 try { PreparedStatement ps conn.prepareStatement(UPDATE options SET countcount1 WHERE id?); ps.setInt(1, optionId); ps.executeUpdate(); conn.commit(); // 显式提交 } catch (Exception e) { conn.rollback(); // 显式回滚 throw e; } }4. 管理后台实战从manage.jsp到delete.jsp的全链路操作与防误删机制4.1manage.jsp的数据驱动逻辑如何从MDB读出议题并生成动态列表manage.jsp是管理员视角的核心页面。它不靠硬编码而是动态查询% page importjava.sql.* % % page importDBBean % % DBBean db new DBBean(); ResultSet rs db.executeQuery(SELECT * FROM topics ORDER BY id); % h2当前议题列表/h2 table border1 trthID/thth议题名称/thth操作/th/tr % while(rs.next()) { % tr td% rs.getInt(id) %/td td% rs.getString(name) %/td td a hrefinfo.jsp?id% rs.getInt(id) %查看统计/a | a hrefdelete.jsp?id% rs.getInt(id) %删除/a /td /tr % } % /table a hrefadd.jsp新增议题/a关键点info.jsp?id1和delete.jsp?id1都通过URL传参。这意味着delete.jsp必须做ID合法性校验否则恶意用户可构造delete.jsp?id-1或id1 OR 11虽原SQL无参数化但Access对数字型ID注入容忍度低。4.2delete.jsp的防误删三重校验URL参数、Session权限、二次确认原代码仅做简单ID检查极易误删。我加了三重防护% page importDBBean % % // 1. Session权限校验必须是管理员 if(session.getAttribute(admin) null || !session.getAttribute(admin).equals(true)) { response.sendRedirect(login.jsp); return; } // 2. URL参数校验必须是正整数 String idStr request.getParameter(id); if(idStr null || !idStr.matches(\\d)) { out.print(非法ID参数); return; } int id Integer.parseInt(idStr); // 3. 二次确认前端JS弹窗 % script if(!confirm(确定要删除议题ID % id %此操作不可恢复)) { history.back(); } /script % // 执行删除 DBBean db new DBBean(); db.executeUpdate(DELETE FROM topics WHERE id id); db.executeUpdate(DELETE FROM options WHERE topic_id id); % 删除成功a hrefmanage.jsp返回管理页/a参数说明idStr.matches(\\d)用正则确保ID是纯数字堵住SQL注入入口history.back()让用户能撤回DELETE FROM options WHERE topic_id确保关联选项一并清除避免孤儿数据。4.3info.jsp的统计可视化用HTML表格呈现投票结果含百分比计算info.jsp展示某议题下各选项得票数及占比。原代码只列数字我补了百分比计算% String idStr request.getParameter(id); int topicId Integer.parseInt(idStr); DBBean db new DBBean(); // 查询总票数用于算百分比 ResultSet totalRs db.executeQuery(SELECT SUM(count) as total FROM options WHERE topic_id topicId); totalRs.next(); int totalVotes totalRs.getInt(total); // 查询各选项 ResultSet optionRs db.executeQuery(SELECT name, count FROM options WHERE topic_id topicId); % h2议题统计% topicId %/h2 table border1 trth选项/thth票数/thth占比/th/tr % while(optionRs.next()) { String name optionRs.getString(name); int count optionRs.getInt(count); double percent totalVotes 0 ? (double)count / totalVotes * 100 : 0; % tr td% name %/td td% count %/td td% String.format(%.1f%%, percent) %/td /tr % } % /table避坑点totalVotes可能为0新议题无投票必须判空否则count/0报ArithmeticException。String.format(%.1f%%, percent)保证百分比保留一位小数视觉更专业。5. 避坑指南五个血泪经验总结现象→原因→解决全是学生高频翻车点5.1 现象访问http://localhost:8080/vote/显示404但http://localhost:8080/vote/index.jsp能打开原因web.xml中welcome-file-list未配置index.jsp或配置了但拼写错误如写成index.html。Tomcat 默认只找index.html、index.htm、index.jsp顺序匹配缺一不可。解决打开vote/WEB-INF/web.xml确认welcome-file-list内容为welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list并确保文件名大小写完全一致Linux下Index.jsp≠index.jsp。5.2 现象登录login.jsp后跳转manage.jsp但页面空白控制台无报错原因process.jsp中session.setAttribute(admin, true)执行了但manage.jsp开头未加% page sessiontrue %。JSP默认开启session但某些Tomcat版本或IDE插件会禁用导致session.getAttribute(admin)返回null。解决在manage.jsp第一行添加% page sessiontrue % % if(session.getAttribute(admin) null || !session.getAttribute(admin).equals(true)) { response.sendRedirect(login.jsp); return; } %5.3 现象点击“投票”按钮无反应vote.jsp页面空白数据库计数未增加原因vote.jsp中request.getParameter(option)获取的值为空。常见于index.jsp的投票表单未正确提交——原代码用a hrefvote.jsp?option1但Access数据库中选项ID是自增数字而index.jsp生成的链接可能ID错位如议题有3个选项但链接写成option4。解决在index.jsp投票链接生成处确保ID来自数据库查询% ResultSet options db.executeQuery(SELECT id, name FROM options WHERE topic_id topicId); while(options.next()) { % a hrefvote.jsp?option% options.getInt(id) %% options.getString(name) %/abr % } %5.4 现象add.jsp提交后报java.sql.SQLException: [Microsoft][ODBC Microsoft Access Driver] Syntax error in INSERT INTO statement.原因add.jsp中topicName包含单引号如输入“学生选课”导致SQL语句变成INSERT INTO topics (name) VALUES (学生选课)引号不匹配。原代码未做转义。解决在add.jsp获取参数后用topicName.replace(, )转义单引号Access标准转义方式% String topicName request.getParameter(topic); topicName topicName.replace(, ); // 关键 DBBean db new DBBean(); db.executeUpdate(INSERT INTO topics (name) VALUES ( topicName )); %5.5 现象info.jsp报java.lang.NumberFormatException: For input string: 原因URL参数id为空Integer.parseInt()抛异常。常见于用户手动修改URL为info.jsp?id或info.jsp?idnull。解决在info.jsp开头加健壮性判断% String idStr request.getParameter(id); if(idStr null || idStr.trim().isEmpty()) { out.print(错误未指定议题ID); return; } int topicId Integer.parseInt(idStr.trim()); %6. 进阶技巧把Access MDB无缝迁移到MySQL含建表SQL、JDBC驱动切换、DBBean重构6.1 Access到MySQL的表结构映射字段类型与主键迁移要点vote.mdb有两张核心表topics议题和options选项。迁移到MySQL需注意三点Access字段MySQL等效类型迁移注意点id(AutoNumber)INT PRIMARY KEY AUTO_INCREMENTAccess的AutoNumber在MySQL中对应AUTO_INCREMENT但需显式设PRIMARY KEYname(Text)VARCHAR(255)Access Text默认65535字符MySQLTEXT类型不支持索引课程设计用VARCHAR(255)更稳妥count(Number)INT DEFAULT 0Access Number默认双精度MySQL用INT足够加DEFAULT 0避免NULLMySQL建表SQL保存为create_tables.sqlCREATE DATABASE IF NOT EXISTS vote_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE vote_db; CREATE TABLE topics ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE options ( id INT PRIMARY KEY AUTO_INCREMENT, topic_id INT NOT NULL, name VARCHAR(255) NOT NULL, count INT DEFAULT 0, FOREIGN KEY (topic_id) REFERENCES topics(id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;为什么用utf8mb4支持emoji和生僻汉字避免?乱码。Access默认ANSI编码导出数据时需用工具如Access Database Engine导出为UTF-8 CSV。6.2 JDBC驱动切换从UCanAccess到MySQL Connector/J含连接池配置替换DBBean.java的驱动和URL// 原Access连接UCanAccess // Class.forName(net.ucanaccess.jdbc.UcanaccessDriver); // String url jdbc:ucanaccess://path/to/vote.mdb; // 新MySQL连接需mysql-connector-java-8.0.33.jar放入WEB-INF/lib/ Class.forName(com.mysql.cj.jdbc.Driver); String url jdbc:mysql://localhost:3306/vote_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue; String username root; String password your_password; // 生产环境勿硬编码 return DriverManager.getConnection(url, username, password);参数说明useSSLfalse关闭SSL本地开发免证书serverTimezoneAsia/Shanghai解决时区问题allowPublicKeyRetrievaltrue兼容MySQL 8.0新认证协议。6.3 DBBean重构引入PreparedStatement防御SQL注入以addTopic为例原代码拼接SQL极不安全。重构后public void addTopic(String topicName) throws Exception { String sql INSERT INTO topics (name) VALUES (?); try (Connection conn getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, topicName); // 自动转义单引号 ps.executeUpdate(); // 获取自增ID供后续插入options用 ResultSet rs ps.getGeneratedKeys(); if (rs.next()) { int topicId rs.getInt(1); // 可继续插入options... } } }关键改进PreparedStatement不仅防注入还提升性能预编译try-with-resources自动关闭连接杜绝资源泄漏Statement.RETURN_GENERATED_KEYS获取新记录ID替代Access的SELECT IDENTITY。6.4 数据迁移实操用Navicat或命令行导入Access数据到MySQL推荐Navicat图形化适合学生在Navicat中新建MySQL连接创建vote_db数据库右键vote_db→ “导入向导” → 选择vote.mdb文件映射表topics→topicsoptions→options字段映射id→id设为主键自增name→name设VARCHARcount→count设INT执行导入。命令行方案Linux/macOS# 1. 导出Access为CSV需mdbtools mdb-export -D %Y-%m-%d %H:%M:%S vote.mdb topics topics.csv mdb-export -D %Y-%m-%d %H:%M:%S vote.mdb options options.csv # 2. MySQL导入注意字段分隔符 mysql -u root -p vote_db -e LOAD DATA INFILE /path/to/topics.csv INTO TABLE topics FIELDS TERMINATED BY , OPTIONALLY ENCLOSED BY \ LINES TERMINATED BY \n (name); 血泪经验mdb-export输出的CSV首行是字段名MySQLLOAD DATA默认跳过首行但Access导出可能含BOM头导致第一行乱码。用vim topics.csv查看若首行开头是^用sed 1s/^\xEF\xBB\xBF// topics.csv topics_clean.csv清除BOM。从那以后我每次接手课程设计项目都强制走一遍“MDB路径校验→JDBC驱动版本核对→web.xml welcome-file检查→manage.jsp基础数据渲染→delete.jsp防误删测试”这五步。不是为了炫技是避免在答辩前两小时发现vote.mdb被误删、或者DBBean里Class.forName写错包名这种低级错误。这套投票系统它不完美但它真实——真实到每个空格、每个分号、每个rs.next()都可能成为你交作业前的最后一道坎。希望帮到你。本文还有配套的精品资源点击获取
返回列表