
简介一份基于JSPAccess的手机销售系统毕业设计完整资料包面向计算机相关专业学生解决毕业设计或课程设计从选题、需求分析、系统设计、论文撰写到答辩材料准备全流程缺资料、不完整的问题。资源包约3.03MB包含论文、系统源码、开题报告、封面扉页、需求分析、任务书等材料构成一套完整可参考的B2C网上购物系统项目。系统围绕商品目录、用户注册、网上订货、购物车、订单管理、库存查询与后台数据库管理展开核心功能包括24小时在线订购服务、商品信息维护与管理、科学灵活的商品分类存储、强大快捷的查询、订单号模块并在订单流程中融入客户商业逻辑规则针对不同客户属性提供不同付款方式。描述中还给出B2C、网上购物系统、JSP、数据库等关键词可直接作为功能设计、论文框架与代码编写的参考。目前已有63人浏览学习适合需要快速梳理JSPAccess项目结构、获取完整毕业设计模板与答辩文档的学生使用。1. 从打包结构看 JSPAccess 手机销售系统的真实交付边界一个毕业设计项目的打包标题里同时含有“论文、系统、开题、封面、扉页、需求分析、任务书”这本身就说明 JSPAccess 手机销售系统不是只写代码的工程而是一套需要你从头能讲、能改、能演示的完整交付物。JSP 负责把手机列表、购物车、订单渲染成 HTMLAccess 以单文件数据库的形式保存商品与订单数据Servlet 和 JavaBean 夹在中间处理请求与业务规则。拿到这类项目后常见做法是先跑通 Tomcat 下的登录页面再对照需求分析文档反推数据库表和页面跳转最后把论文里的 ER 图、测试用例与代码逐一对应。整套技术栈的优势是部署轻、入门快但驱动位数、文件独占锁、SQL 方言差异这些问题往往要到真正演示时才暴露出来。2. 用 UCanAccess 打通 JSP 到 Access 的连接配置与最小运行实例2.1 老教材里的 JDBC-ODBC 桥为什么在 64 位环境下频繁翻车早期电子书里的 access 数据库教程驱动加载代码几乎都是Class.forName(sun.jdbc.odbc.JdbcOdbcDriver)再拼一个jdbc:odbc:PhoneSales。这条路在 JDK 8 正式移除 JDBC-ODBC Bridge 之后已经走不通在 JDK 8 环境运行第一行就会抛ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver。如果强行退回 JDK 7 并在 64 位 Windows 上配 ODBC 数据源Tomcat 进程大概率会直接崩溃退出事件查看器里能看到0xc0000005对应的 access violation也就是热词里process exited with code 3221225477这个数字——它正好是 0xC0000005 换算成十进制的值。这类崩溃的根因是 ODBC 桥调用本机 odbc32.dll 时发生非法内存访问跟业务代码没有关系也不是重启就能好的。网上不少 access 数据库教程还停留在 JDBC-ODBC 桥时代光下载一个 access 数据库64位系统驱动程序也救不回来因为 JDK 8 已经把桥从标准库中整段移除驱动位数和 ODBC 数据源位数不一致时连 32 位数据源列表都看不到。所以现在我接到 .accdb/.mdb 文件统一用 UCanAccess。它基于 Jackcess 用纯 Java 读写 Access不依赖 ODBC 数据源JDK 8 到 17 都能用。如果确实只能在 JDK 5/6/7 上跑才考虑 32 位 JDK 配 32 位 ODBC 的老方案而且系统 ODBC 管理工具也要切成 32 位。ODBC 数据源会读注册表换一台机器就要重新配置UCanAccess 只把数据库文件当作普通文件读取Java 进程能读到文件就能连环境迁移省事得多。2.2 UCanAccess 驱动包、连接串与参数说明如果是 Maven 工程依赖写法如下!-- ucanaccess 5.x 缺省打包了 jackcess/hsqldb 等匹配依赖 -- dependency groupIdnet.sf.ucanaccess/groupId artifactIducanaccess/artifactId version5.0.1/version /dependency非 Maven 部署时把主 Jar 连同 commons-lang3、commons-logging、jackcess-core、hsqldb 一并复制到WEB-INF/lib。UCanAccess 的 5.x 版已经内置这些依赖的匹配版本不需要再从别处找兼容包。连接串按下面这种方式写String url jdbc:ucanaccess:///D:/PhoneSales/data/phones.accdb;memoryfalse;singleConnectiontrue;参数有三个关键点memoryfalse是默认值SQL 直接作用到文件适合演示场景memorytrue会把表加载进内存写操作后必须显式压缩中途断电容易丢数据答辩现场不适合。singleConnectiontrue让同一个 JVM 的连接复用同一个底层文件句柄能减少 Access 文件被多连接打开时的锁冲突。还可以加shrinktrue在连接释放后压缩数据库但如果此时 Access 桌面版打开了同一个文件压缩会抛异常所以演示前记得关掉 Office。2.3 最小可复现的 JSP 查询页与数据库文件放置路径% page importjava.sql.* % % page contentTypetext/html;charsetUTF-8 % % // 数据库放 WEB-INF 下既能定位又不会被浏览器直接下载 String url jdbc:ucanaccess:// application.getRealPath(/WEB-INF/phones.accdb) ;singleConnectiontrue; String sql SELECT brand, model, price FROM phone WHERE stock 0; try (Connection conn DriverManager.getConnection(url); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql)) { while (rs.next()) { out.println(p rs.getString(brand) rs.getString(model) rs.getBigDecimal(price) /p); } } catch (SQLException e) { out.println(p连接失败 e.getMessage() /p); } %代码里application.getRealPath(/WEB-INF/phones.accdb)取到的是部署后的绝对路径把数据库放进 WEB-INF 后浏览器无法通过 URL 直接下载 .accdb 文件。Class.forName被省略是因为驱动 Jar 的META-INF/services会被DriverManager自动发现写不写都行。getBigDecimal读取货币字段避免用 getFloat 出现金额浮点误差。如果你查过jsp编译class文件保存在哪里Tomcat 会在work/Catalina/localhost/工程名/org/apache/jsp目录下生成 JSP 对应的 Java 与 class 文件页面报 500 时去这里看生成源码能定位到具体是哪一行 Scriptlet 出错。常见报错按下面这张表定位最快现场报错根因处理办法ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriverJDK 8 已移除旧桥接驱动换 UCanAccess驱动类改为自动发现process exited with code 3221225477 / 0xc000000564 位环境 ODBC 桥内存访问违规删除 ODBC 数据源全部改用 UCanAccessThe process cannot access the file because...Access 文件被 Office 或另一进程独占关闭 Office 打开的文件taskkill /F /IM javaw.exe清僵尸进程提示UCanAccess 支持readOnlytrue参数缓解文件占用问题但演示前最稳妥的仍然是只开一个 Tomcat 实例并确保没有在桌面版 Access 里打开同一个数据库文件。3. 手机销售系统的表结构、登录与购物车实现3.1 用 5 张表撑起一个可答辩的手机销售系统Access 里的表结构我一般这样建表名关键字段Access 类型说明adminadmin_id, username, pwd自动编号, 短文本, 短文本后台管理员phonephone_id, brand, model, price, stock自动编号, 短文本, 短文本, 货币, 长整型手机主表customercustomer_id, username, pwd, phone_no, address自动编号, 短文本, 短文本, 短文本, 长文本注册客户ordersorder_id, order_no, customer_id, total, create_time自动编号, 短文本, 长整型, 货币, 日期/时间订单主表order_itemitem_id, order_id, phone_id, qty, price自动编号, 长整型, 长整型, 整型, 货币订单明细字段命名有两个要避开的地方order、user、name在 Access 里都有保留字风险表名用orders客户名字段用username或real_name。金额字段用货币因为双精度保存价格会出现精度抖动排序和汇总时尤其明显。自增字段用 Access 的自动编号注意它不能像 MySQL 那样指定起始值插入主记录后要用 JDBC 的getGeneratedKeys()取回订单号。3.2 登录接口与个人信息展示页的 Session 校验登录接口用 PreparedStatement 处理String sql SELECT admin_id, username FROM admin WHERE username? AND pwd?; try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, request.getParameter(username)); ps.setString(2, request.getParameter(pwd)); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { request.getSession().setAttribute(loginAdmin, rs.getString(username)); response.sendRedirect(admin_phone_list.jsp); } else { response.sendRedirect(login.jsp?error1); } } }参数占位符?配合setString保证用户输入的引号、分号只作为字符串内容解析不会拼接进 SQL 语义里这是预防 SQL 注入的最小做法。如果写成字符串拼接数据里出现单引号就会让整条语句失控安全扫描一抓一个准。对应到用户个人中心这类 jsp 个人信息展示页面顶部统一做 Session 校验% String loginUser (String) session.getAttribute(loginUser); if (loginUser null) { response.sendRedirect(login.jsp?timeout1); return; // 阻止后续页面继续输出 } %return语句保证未登录时直接跳转、后面的页面代码不再执行。更规范的做法是抽一个 Filter 统一拦截/user/*和/admin/*路径但课程设计里先在需要保护的页面各写一遍再抽取成公共 JSP 片段也不丢分。3.3 手机列表分页与按品牌筛选的 SQL 写法UCanAccess 内置 HSQLDB 风格的 SQL parser支持LIMIT/OFFSET分页比老式 Access 的TOP NOT IN简洁SELECT phone_id, brand, model, price, stock FROM phone ORDER BY phone_id LIMIT 5 OFFSET 0分页必须有稳定的ORDER BY否则结果可能重复或跳行。第二页时把偏移改成 5第三页改成 10。Java 侧计算int pageNo 1; int pageSize 5; try { pageNo Integer.parseInt(request.getParameter(page)); } catch (NumberFormatException e) { pageNo 1; } if (pageNo 1) pageNo 1; int offset (pageNo - 1) * pageSize;再加品牌筛选时SQL 用WHERE 11动态拼接条件String sql SELECT * FROM phone WHERE 11; if (brand ! null !brand.isEmpty()) sql AND brand?; if (keyword ! null !keyword.isEmpty()) sql AND model LIKE ?; // 通配符 % 放在参数值里而不是拼进 SQL 文本 PreparedStatement ps conn.prepareStatement(sql); int idx 1; if (brand ! null !brand.isEmpty()) ps.setString(idx, brand); if (keyword ! null !keyword.isEmpty()) ps.setString(idx, % keyword %);把%放进参数值而不是拼进 SQL是为了让?占位符仍然覆盖整条动态片段LIKE 的转义字符也按参数处理避免输入%或_时改变查询语义。3.4 从购物车到订单事务边界和库存扣减购物车用 Session 里的 Map 保存HttpSession session request.getSession(); MapInteger, Integer cart (MapInteger, Integer) session.getAttribute(cart); if (cart null) { cart new LinkedHashMap(); } cart.merge(Integer.parseInt(request.getParameter(phoneId)), 1, Integer::sum); session.setAttribute(cart, cart);merge的三个参数分别是键、增量、合并函数Integer::sum表示同一部手机第二次加入时在原有数量上加 1。用LinkedHashMap保持加购顺序结算页展示时不会乱跳。订单生成放在一个事务里conn.setAutoCommit(false); try { PreparedStatement psOrder conn.prepareStatement( INSERT INTO orders (order_no, customer_id, total, create_time) VALUES (?,?,?,?), Statement.RETURN_GENERATED_KEYS); psOrder.setString(1, NO System.currentTimeMillis()); psOrder.setInt(2, customerId); psOrder.setBigDecimal(3, total); psOrder.setTimestamp(4, new Timestamp(System.currentTimeMillis())); psOrder.executeUpdate(); ResultSet keys psOrder.getGeneratedKeys(); keys.next(); int orderId keys.getInt(1); // 逐条插入 order_item并 UPDATE phone SET stock stock - ? WHERE phone_id ? AND stock ? conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; }Statement.RETURN_GENERATED_KEYS与getGeneratedKeys()是 JDBC 标准方式在 Access/UCanAccess 下同样适用。库存更新 SQL 多写一个AND stock ?执行后若updateCount 0说明库存不足抛出异常让整个事务回滚。这个场景最核心的是原子性订单主表、明细表、库存表要么一起成功要么一起失败绝不允许只扣了库存却生成不了订单。4. 论文、开题、需求分析与 JSP 系统代码的对应关系4.1 需求分析文档里的功能边界怎么划分需求分析里最容易出问题的是把所有角色揉成一个。正确的划分是先给三类角色游客浏览手机列表、按品牌和关键词搜索、查看详情、注册客户在游客基础上增加登录、购物车、提交订单、查看自己的订单记录管理员维护手机信息、处理订单状态、查看客户列表。然后用一张表把它们钉死后续论文的模块设计、数据库设计、页面清单都基于这张表展开需求分析中的功能点对应 JSP 页面涉及数据表 / 存储游客浏览手机列表index.jsp, phone_list.jspphone按品牌、价格筛选phone_list.jsp?brandHuaweiphone用户注册与登录register.jsp, login.jspcustomer个人信息展示center.jspcustomer, orders购物车管理cart.jspHTTP Session生成订单order_confirm.jsporders, order_item后台手机管理admin_phone_list.jsp, admin_phone_edit.jspphone, admin订单状态更新admin_order_list.jsporders购物车不落表所以用 HTTP Session 标识存储位置。把这句话写进需求分析能解释清楚为什么重启 Tomcat 后购物车会清空让老师挑不出边界矛盾。4.2 论文中的 ER 图、用例图、数据字典如何从现有表跑出来数据库已经建好在 Access 里先打开 Access 的关系视图把 5 张表拉进去在 phone、customer、orders 之间建立关系Access 自己就能生成接近 ER 图的关系图导出后用 draw.io 补上基数标记。用例图三个角色加八种动作动作必须和 4.1 的功能点一一对应。数据字典的常见问题是字段类型写错。Access 的货币在论文里应描述为DECIMAL(10,2)自动编号是INT 自增主键日期/时间对应DATETIME/TIMESTAMP。order_no、create_time这类字段要标默认值order_no默认值可以是NO Now()create_time默认值是Now()。对应的 Access DDL 是-- Access 的保留字避开 order因此表名用 phone CREATE TABLE phone ( phone_id AUTOINCREMENT PRIMARY KEY, brand TEXT(50), model TEXT(100), price CURRENCY, stock INTEGER );AUTOINCREMENT对应 MySQL 的AUTO_INCREMENTCURRENCY对应DECIMAL(10,2)UCanAccess 下这条语句可以直接执行也是数据字典最可靠的来源。4.3 任务书与开题报告的技术路线描述框架任务书里需要明确技术选型B/S 架构JSPServletJavaBean 分层数据库选 Access驱动选 UCanAccess运行环境是 JDK 8 / Tomcat 8.5。开题报告把每一层写具体表现层由 JSP 页面构成负责渲染与简单表单校验控制层由 Servlet 接收请求并调用业务类业务层用 JavaBean 封装登录、购物车、订单生成、库存扣减方法数据层用 DAO 封装 JDBC 操作通过 UCanAccess 驱动连接 phones.accdb。进度安排不要写“开发调试”这种模糊词要用表格写清第几周完成哪个交付物第 1 周需求分析、第 2 周数据库与页面原型、第 3-5 周编码、第 6-7 周测试与文档、第 8 周答辩演练。评阅老师拿到任务书先看进度表再看技术路线这两块写不满会被直接要求改。4.4 论文测试章节与演示脚本的绑定系统测试章节最稳的写法是先列功能测试用例再给回归测试表格。功能测试表至少包含用例编号、模块、前置条件、操作步骤、预期结果、实际结果。举两个例子用例编号模块前置条件操作步骤预期结果TC-SALE-01订单手机A库存10已登录客户下单3台并提交订单总金额正确库存变为7TC-RETURN-01订单管理员登录后台将订单状态改为已发货订单列表状态更新演示脚本固定为 15 分钟链路游客访问首页→搜索“华为”→注册并登录→加入购物车→结算生成订单→管理员登录→修改手机价格→在订单列表标为已发货。每演示完一步打勾被打断也能快速找回断点。脚本里要准备两类恢复手段一是预置一个库存为 100 的测试账号避免演示时库存耗尽二是准备一段重置订单状态的 SQL出问题时 30 秒内恢复现场。5. 手机销售系统答辩前十分钟验证清单与 5 个高频报错处置5.1 演示前验证顺序答辩前按下面这张表过一遍能覆盖 90% 的现场问题检查项操作或命令通过标准JDK 版本java -version与 Servlet 编译版本匹配端口占用netstat -ano | findstr :8080Tomcat 进程占用无残留 javaw驱动存在看WEB-INF/lib下 ucanaccess*.jar文件存在且非 0 字节数据库解锁项目目录无.laccdb锁文件文件可复制、可改名核心链路登录→加购→结算→后台改价每一步跳转符合预期特别提醒Maven 打包前先执行 package再检查target/xxx/WEB-INF/lib里有没有 UCanAccess不然换机器部署时第一分钟就开始报驱动找不到。5.2 现场不能慌的 5 个报错与处置第一个是ClassNotFoundException: net.ucanaccess.jdbc.UcanaccessDriver驱动 Jar 没进 WEB-INF/lib把 Jar 复制进去后重启 Tomcat。第二个是process exited with code 3221225477 / 0xc0000005这类 JVM 崩溃多出现在旧代码残留 ODBC 配置检查代码里没有jdbc:odbc:连接串并保证 Tomcat 与 JDK 都是 64 位。第三个是error 1045 access denied for user rootlocalhost通常是搜索答案时误把 MySQL 连接串里的账号密码填进 Access 工程处理办法是把 UCanAccess 的 URL 换成无账号密码的文件路径。第四个是 JSP 页面改动不生效因为 JSP 编译后的 class 缓存在 Tomcat work 目录删除work/Catalina/localhost/工程名后重启即可。第五个是库存出现负数说明下单时先查库存再更新缺少UPDATE ... WHERE stock ?这个并发保护条件也就是 3.4 节事务代码解决的问题。最后补一个偏交互的加分技巧在手机编辑页加上beforeunload未保存提示也就是很多人搜索的“屏蔽jsp离开页面提示”场景。JSP 本身不参与浏览器弹窗逻辑只负责渲染下面这段脚本let dirty false; document.querySelectorAll(input, select).forEach(el { el.addEventListener(change, () { dirty true; }); }); window.addEventListener(beforeunload, e { if (dirty) { e.preventDefault(); e.returnValue ; // 触发浏览器的离开确认框 } });dirty标志在表单发生编辑后置为 truebeforeunload事件返回空字符串就会弹出离开确认框。演示“修改价格后直接点导航离开”时页面会拦下来这个细节能让评委认为你考虑了数据丢失风险。把这段脚本抽成 common_guard.jsp 公共片段需要保护的编辑页 include 进去就能复用。本文还有配套的精品资源点击获取