ARTICLE DETAIL

资讯详情

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

JSP与SQL Server网上花店系统:从三层架构到订单事务的实战解析

JSP与SQL Server网上花店系统:从三层架构到订单事务的实战解析 简介JSP与SQL Server是早期Java Web课程设计的经典技术组合至今仍是许多学习者理解前后端数据交互的入门路径。一个典型的三层结构里JSP页面负责展示与请求分发JavaBean封装业务实体而SQL Server则承担结构化存储与关联查询。通过JDBC驱动建立连接后页面才能完成用户注册、商品浏览、购物车管理及订单提交等闭环操作。其中购物车通常借助Session保存商品ID与数量订单提交则依赖事务保证主表与明细表的原子性写入。此外开发过程中常遇到SQL Server实例配置、字符串转数字、行转列聚合等技术点这些细节既是排错关键也是提升工程能力的实用突破口。本文以一套网上花店系统为线索梳理从数据库建模到核心链路实现的完整路径为复现项目或快速上手这类技术栈提供参考。1. 还在被反复搜索JSP SQL Server 网上花店系统到底能带来什么一套JSP网上花店系统论文12011字附SQL Server数据库和源码听起来像新手练手题真正动手能卡住两周。每年到了毕设节点搜“jsp sqlserver 源代码 网上花店系统”的人都会多起来因为课程设计允许的技术栈里这套组合最磨人老环境不好装、驱动版本容易冲突、代码里还带着早期ASP写法的影子。这份资源的价值在于它把论文、页面、Bean类、数据库脚本全打在一个包里需求不用靠猜论文开篇就把功能清单写清楚了。它本质上是一条完整的旧时代电商链路注册登录、商品浏览、购物车、订单处理、支付接口集成。现在去研究它不是为了学新语法而是为了在一个规模足够小的系统里把从页面到数据库的整条数据流走通后面换SSM、Spring Boot走的还是同样的骨架。适合两类人一类是毕业设计要复现、要答辩的学生另一类是接单或搭内网演示系统的开发者想要一个不会超出掌控的模板。下文会把包内文件逐项拆开先说架构和建模再谈购物车与订单提交最后把SQL Server 2012到2019环境下最容易翻车的点列清楚。2. 项目骨架与三层架构JSP页面、Bean类与SQL Server库表各管哪一摊2.1 包内文件结构先分清 menu.asp、top.asp、class 文件之间的关系解压后先看清单你会发现一个有意思的点里面既有menu.asp、top.asp这种按ASP习惯命名的公共页也有GoodsBean.class、Cart.class、UserBean.class这类JavaBean再加上ProduceBean.class第一眼容易懵。我把这类包的阅读顺序固定为三步先找入口页面再看页面引用了哪些Bean类最后顺着Bean找数据库表。虽然文件名带.asp后缀但要注意资源核心其实仍是JSP体系许多老课程设计从ASP迁移到JSP时会把top.asp、menu.asp当作公共模块名保留下来真正在Tomcat里跑的时候JSP引擎照常解析。整个包的角色可以归纳成一张表文件关键词预期角色在系统里承担什么top.asp公共页头显示站名、登录状态、导航入口menu.asp分类导航从商品分类表动态读取菜单GoodsBean.class商品实体封装 goods 表字段供页面读写ProduceBean.class商品/产品实体部分列表页用它做商品信息载体Cart.class购物车实体维护购买项存在 session 里UserBean.class用户实体封装 users 表字段与登录状态读取逻辑上这套代码没有框架注解也不做MVCN分层一切靠 session 和 request 传对象。好处是依赖极少Tomcat 6/7 就能跑坏处是页面脚本和业务逻辑混在一起改错一个强转就会整页500。我第一次拆这种包时习惯先把menu.asp和top.asp里所有include指令找出来因为页头引用出错会导致整个站点所有页面一起打不开。2.2 数据库建模还原商品表、用户表、订单表的关键字段设计论文摘要里明确说系统涉及商品表、用户表、订单表及关联关系这部分是理解整个项目运转的支点。老项目建模通常不复杂但字段类型的选择直接决定后面会不会踩坑。商品表goods的常见结构字段名类型约束说明goods_idint identity(1,1)主键商品编号goods_namevarchar(50)非空花名或商品名category_idint外键关联分类表pricedecimal(18,2)0销售单价stockint0库存数量goods_picvarchar(100)可空商品图片路径sales_countint默认0已售数量用户表users的常见结构字段名类型约束说明user_idint identity(1,1)主键用户编号usernamevarchar(30)唯一非空登录账号passwordvarchar(50)非空密码密文phonevarchar(20)可空联系电话addressvarchar(120)可空收货地址reg_timedatetime默认 getdate()注册时间订单与明细则是经典的一对多orders主表记录订单号、用户、总金额、支付状态order_detail子表记录每个商品的数量和“成交时单价”。很多新手写订单时会直接去goods表查价格当展示价这是不对的订单明细里必须冗余一份unit_price否则后面商品改价历史订单金额就全乱了。价格字段统一用decimal(18,2)不要用float这是老项目里最容易翻车的选择。float是浮点型格式化输出时可能出现 19.99 变成 20.00000001 的情况decimal(18,2)在SQL Server里是定点数最适合存金额。库存字段用int就够花店这种低频业务不需要上bigint。2.3 JDBC连接与数据库配置驱动、连接串、登录方式的取舍代码包里通常会有一个数据库连接辅助类职责就三件事加载驱动、获取连接、关闭资源。老项目里这个类一般写成工具类核心代码长这样package util; import java.sql.*; public class DBHelper { private static final String DRIVER com.microsoft.sqlserver.jdbc.SQLServerDriver; private static final String URL jdbc:sqlserver://localhost:1433;DatabaseNameflower_shop;encryptfalse; private static final String USER sa; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConn() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(ResultSet rs, Statement st, Connection conn) { try { if (rs ! null) rs.close(); } catch (SQLException ignored) { } try { if (st ! null) st.close(); } catch (SQLException ignored) { } try { if (conn ! null) conn.close(); } catch (SQLException ignored) { } } }逻辑说明Class.forName(DRIVER)在类加载阶段注册驱动这一步失败通常说明sqljdbc.jar没放进WEB-INF/lib或 Tomcat 的lib目录。连接串里DatabaseNameflower_shop是库名要和你实际建库的名称一致encryptfalse是给 SQL Server 2018以后版本用的老驱动不带这个参数时连 SQLServer 2019 会直接报 TLS 相关错误。参数说明USER和PASSWORD一般默认用sa账号对应 SQL Server 混合认证模式。如果想用 Windows 集成认证需要把integratedSecuritytrue加进URL并在本机装对应DLL老项目通常不提供这一步所以复现这个资源时建议直接建sa账号并设置密码省事很多。注意驱动版本和数据库版本要匹配。SQL Server 2012 时代常见的是sqljdbc4.jar如果数据库是 2016 或 2019下载新版的mssql-jdbc驱动更稳妥老驱动偶发“无法找到数据库引擎启动句柄”类问题其实并不是数据库本身坏了而是驱动不认识新实例的服务状态。3. 从登录到订单网上花店核心链路如何跑通3.1 用户注册与个人信息展示session 状态管理和表单校验网上花店的注册登录流程论文里定性为系统基础功能。实现上登录成功后把用户对象放入 session后续页面靠判断 session 是否为 null 来决定跳登录页还是放行。一个典型的个人信息展示页面对应热词里的“jsp个人信息展示页面”可以这样写% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 % % UserBean user (UserBean) session.getAttribute(loginUser); if (user null) { response.sendRedirect(login.jsp); return; } % html body div classuser-info h3欢迎你%user.getUsername()%/h3 p联系电话%user.getPhone()%/p p收货地址%user.getAddress()%/p p注册时间%user.getRegTime()%/p /div /body /html逻辑说明页面先从 session 里取loginUser取不到就重定向到登录页。这一步是整套系统的访问控制基础论文里提到的用户认证就是靠这个机制完成。要注意session.getAttribute返回的是 Object必须强转为UserBean否则页面直接 500。参数说明response.sendRedirect是重定向会让浏览器重新请求 login.jsp如果只用return不跳转页面主体还会继续渲染等于白做了校验。实际操作中登录成功后要执行session.setAttribute(loginUser, userBean)并且建议session.setMaxInactiveInterval(30 * 60)把会话超时设为30分钟。这里还有一个老项目常见问题password字段在数据库是varchar如果页面里直接request.getParameter(password)去比对明文安全性很差。论文阶段一般不做加密但稍微正规一点的复现建议在注册时用MD5或SHA-256把密码摘要后落库登录时比对摘要值费用一点时间答辩时反而能多讲一个安全点。3.2 购物车 Cart.class 核心实现数量累加和 session 存取购物车是这个系统的中坚模块原包里Cart.class的角色就是维护一组“商品ID 数量”。如果原包没有附带.java源码按行为重写的话最接近原始风格的代码是package bean; import java.util.HashMap; import java.util.Map; public class Cart { private MapInteger, Integer items new HashMapInteger, Integer(); public void add(int goodsId, int count) { Integer old items.get(goodsId); if (old null) { items.put(goodsId, count); } else { items.put(goodsId, old count); } } public void remove(int goodsId) { items.remove(goodsId); } public void clear() { items.clear(); } public int getTotalCount() { int sum 0; for (int c : items.values()) { sum c; } return sum; } public MapInteger, Integer getItems() { return items; } }逻辑说明购物车用HashMapInteger, Integer存储key 是商品IDvalue 是购买数量。add方法里做了数量累加第二次加入同一商品不会新增一行而是把旧数量加新数量。这是购物车最基础的行为也最符合“往篮子里再丢一朵花”的直觉。参数说明cart对象要放进 session而不是 request。放进 request 的话每次页面跳转购物车就清空放进 application 的话所有用户共用同一个购物车互相干扰。正确的装载点是session.setAttribute(cart, cart)在addCart.jsp里从 session 取出、更新、再放回去。一般通过 URL 传参进入购物车页addCart.jsp?goodsId12count1参数由前端页面拼接。从request.getParameter拿到的count是字符串必须转int这一步会在下一节专门讲因为字符串转数字是JSP项目里报错频率最高的位置之一。3.3 订单提交字符串转数字、事务包裹、明细写入三个环节订单提交在系统里的作用是把购物车数据落库。逻辑分三步遍历购物车项从goods表取出当前价计算订单总金额插入orders表拿到自增订单号再循环插入order_detail明细表。这三步必须在一个事务里完成否则订单主表写成功、明细写失败后台查单会看到一笔“空订单”。对应热词“sqlserver 字符串转数字”JSP 接收参数时最常见的代码是int count Integer.parseInt(request.getParameter(count)); int goodsId Integer.parseInt(request.getParameter(goodsId));逻辑说明request.getParameter永远返回字符串哪怕前端传的是数字Servlet 里拿到手也是2而不是2。Integer.parseInt负责把字符串转成int。老项目频繁在这行报NumberFormatException原因是参数是 null 或空串。更稳的写法是先用String接住再判断String countStr request.getParameter(count); int count 0; if (countStr ! null !countStr.trim().isEmpty()) { try { count Integer.parseInt(countStr.trim()); } catch (NumberFormatException e) { count 1; } }参数说明countStr.trim()去掉了首位空格防止 2 这种带空白字符的输入try-catch兜底转失败就用默认值1。这套防御式写法在课程设计里足够用也比直接parseInt安全一个量级。订单写入部分对应“sqlserver 多行合并成一行”这个高频检索点。老项目要查看某个订单买了哪些商品最常用的 SQL 是把明细多行合并成一个逗号分隔字符串SELECT o.order_id, STUFF(( SELECT , g.goods_name FROM order_detail d JOIN goods g ON d.goods_id g.goods_id WHERE d.order_id o.order_id FOR XML PATH() ), 1, 1, ) AS goods_list, o.total_amount FROM orders o WHERE o.pay_status 0;逻辑说明FOR XML PATH()把查询结果的多行拼成一个长字符串STUFF(..., 1, 1, )去掉开头的逗号。最终输出一行一单、商品清单合并的效果。这是在 SQL Server 里做行转列的经典手法比临时表简单比递归 CTE 直观。插入订单时要用事务保护Java 侧的关键代码Connection conn DBHelper.getConn(); try { conn.setAutoCommit(false); String insertOrder INSERT INTO orders(user_id, total_amount, pay_status) VALUES(?, ?, 0); PreparedStatement psOrder conn.prepareStatement(insertOrder, Statement.RETURN_GENERATED_KEYS); psOrder.setInt(1, userId); psOrder.setBigDecimal(2, totalAmount); psOrder.executeUpdate(); ResultSet rs psOrder.getGeneratedKeys(); int orderId -1; if (rs.next()) { orderId rs.getInt(1); } String insertDetail INSERT INTO order_detail(order_id, goods_id, quantity, unit_price) VALUES(?, ?, ?, ?); PreparedStatement psDetail conn.prepareStatement(insertDetail); for (Map.EntryInteger, Integer entry : cart.getItems().entrySet()) { psDetail.setInt(1, orderId); psDetail.setInt(2, entry.getKey()); psDetail.setInt(3, entry.getValue()); psDetail.setBigDecimal(4, priceMap.get(entry.getKey())); psDetail.addBatch(); } psDetail.executeBatch(); conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { DBHelper.close(null, null, conn); }逻辑说明setAutoCommit(false)是关闭自动提交眼里只有commit()成功后才真正落库。Statement.RETURN_GENERATED_KEYS让getGeneratedKeys()能取到 SQL Server 自增主键addBatch()executeBatch()批量提交明细减少往返次数。参数说明totalAmount类型是BigDecimal对应数据库decimal(18,2)priceMap是在下单前循环查库得到的“商品ID - 当前单价”映射。这一套写下来订单主表和明细分两步落库任何一步失败整体回滚数据不会出现半截状态。答辩时把这讲清楚数据库建模和事务能力都覆盖到了。4. 避坑指南JSPSQLServer项目环境复现的五个高频翻车点4.1 现象SQL Server 2019/2022安装失败报“无法找到数据库引擎启动句柄”很多复现这套资源的电脑上SQL Server 2019 装到一半提示“无法找到数据库引擎启动句柄”或者装完以后 SQL Server 服务起不来Management Studio 连不上。原因最常见的是两个一是安装时选了默认实例而机器上已经有旧实例占用了默认名称MSSQLSERVER二是服务登录账号权限不足导致数据库引擎启动时拿不到句柄。解决换实例名重装比如把默认实例改成SQL2019如果已经装过了打开“SQL Server配置管理器”找到对应实例的“服务”把登录身份改为NT Service\MSSQLSERVER后重启服务。安装前最好把旧实例彻底卸载包括注册表里的MSSQLServer键值否则新老实例互相干扰能折腾一晚上。4.2 现象JDBC连不上报“通过端口1433连接到主机localhost失败”代码里连接串写的是jdbc:sqlserver://localhost:1433但是 Tomcat 一启动就抛这个错。原因SQL Server 默认安装时网络协议里通常只开了 Named Pipes没有开 TCP/IP。JDBC 走的是 TCP 1433 端口两个协议不通自然失败。解决打开“SQL Server配置管理器”展开“SQL Server网络配置”选中对应实例的协议右键TCP/IP选择启用再双击 TCP/IP切到“IP地址”页签把IPALL里的 TCP 端口设为 1433最后重启 SQL Server 服务。命令行验证可以这样判断netstat -ano | findstr 1433如果能看到LISTENING状态说明端口通了再去查防火墙如果没有任何输出说明协议没启用回头再看配置管理器。这一步是 sqlserver 配置管理器安装环节最常见的操作别跳过。4.3 现象Tomcat启动正常访问页面却报500提示 ClassNotFoundException页面一打开就是 500错误日志里出现ClassNotFoundException: bean.GoodsBean或org.apache.jasper.JasperException。原因编译后的.class文件没放在正确的目录或者sqljdbc.jar没进WEB-INF/lib。老项目经常直接把.class丢在工程根目录Tomcat 找不到包结构。解决按包名建目录把GoodsBean.class放到WEB-INF/classes/bean/下而不是classes/根目录。检查方式dir WEB-INF\classes\bean\*.class如果文件在但 Tomcat 还是找不到看是不是WEB-INF/lib里驱动版本冲突。老项目里常见的雷是同时放了sqljdbc4.jar和sqljdbc41.jar两个版本会互相打架保留一个即可。4.4 现象页面上中文正常落库之后变成“”注册用户填的中文地址页面显示正常但 SQL Server 里查出来的全是问号。原因三个环节字符集不一致。JSP 页面是 UTF-8SQL Server 数据库排序规则默认装成了Latin1_GeneralJDBC 连接串上也没指定字符编码。解决第一JSP 页面统一页面声明% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %第二JDBC 连接串后面追加characterEncodingUTF-8第三数据库排序规则改成中文系。已经建错的库用 SQL 修正ALTER DATABASE flower_shop COLLATE Chinese_PRC_CI_AS;参数说明Chinese_PRC_CI_AS是简体中文、大小写不敏感、重音敏感的排序规则老系统默认值一般是这个如果项目里要区分大小写可以改用Chinese_PRC_CS_AS。改完排序规则后已存在的表可能需要重建索引演示阶段数据不多通常执行完就生效。4.5 现象SQL 查询报“对象名 goods 无效”系统登录成功后进商品列表页报对象名 goods 无效SQL Server 本身运行正常。原因大部分情况是连接串里的DatabaseName和实际库名不一致或者sa账号的默认数据库是master查询时没有指定库SQL Server 就跑到master里找goods表自然找不到。解决先把sa的默认数据库改过去ALTER LOGIN sa WITH DEFAULT_DATABASE flower_shop;再检查 JSP 里的 SQL 语句建议全部改成dbo.goods这种带 schema 前缀的写法避免账号默认 schema 不是dbo时的二次踩坑。老代码里经常到处是裸表名这种问题在换了数据库账号后特别容易触发属于典型的“能连上但查不到”的隐蔽故障。5. 让答辩演示一次通过验证清单和三个实用技巧5.1 十二步演示脚本把功能点变成可重复的验证流程拿到这套系统后不建议直接开讲先按下面的清单走一遍确认每个节点都能给出预期结果。这张表也可以直接作为答辩时的操作提纲。步骤操作预期结果1启动 SQL Server 服务和 Tomcat两个服务均无异常2打开首页 index.jsp商品列表正常显示无 5003注册一个新用户提示成功users 表新增记录4使用新账号登录跳转首页顶部显示用户名5点击任一鲜花加入购物车购物车数量变为 16再加另一商品购物车数量变为 2总价累加7修改某项数量总数与总金额同步变化8提交订单orders 表新增一行状态 09模拟支付回调pay_status 变为 110查看订单详情明细行正确显示商品名、数量、单价11清空购物车购物车内项目为零12退出登录session 失效回登录页第 9 步的支付回调是论文里最容易被追问的地方。原资源提到的支付接口集成实际演示时不需要真的对接第三方在本地模拟一个回调页面修改pay_status字段即可。答辩老师关心的是状态流转是否清晰而不是真的看你调微信支付。5.2 改价、加库存、重置数据三句话应对现场问题答辩现场最怕老师问“库存还能改吗”“价格能调吗”。这个系统的数据在数据库里修改很快。商品提价和库存重置直接执行UPDATE goods SET price 59.90 WHERE goods_id 1; UPDATE goods SET stock 1000 WHERE goods_id IN (1, 2, 3);逻辑说明第一条改价格第二条批量改库存。如果想回到初始状态最简单的方式是重跑数据库脚本重建库表。注意先停止 Tomcat 再执行DROP DATABASE flower_shop和重新建库否则连接占用可能导致删除失败。5.3 图片坐标定位与页面加载刷新两个容易让演示加分的细节对应“jsp图片如何对坐标定位”这个搜索点商品列表页的图片定位本质上是 HTML 相对定位问题。旧系统里商品图标存的是upload/flower_01.jpg路径图片没有按比例缩放时容易溢出卡片。常见的修法是在商品卡片上加一个相对定位容器促销标签用绝对定位叠上去div styleposition:relative; width:220px; height:220px; img srcupload/flower_01.jpg width220 height220 alt鲜花 / span styleposition:absolute; top:8px; left:8px; color:#fff; background:#c0392b; padding:2px 6px;热销/span /div逻辑说明父容器position:relative成为定位基准子元素position:absolute的top、left就相对于图片框计算。这套写法不依赖框架JSP 页面直接内联样式就能生效。图片路径则要确认upload目录位于 Web 应用根目录下路径错误时用浏览器开发者工具看 Network 标签404 就说明相对路径差了一层或两层。对应“jsp页面让加载完后刷新一次”这个需求通常出现在支付回调页或订单确认页。最省事的实现是在 JSP 页面 head 区加一个延时刷新meta http-equivrefresh content1;urlcart.jsp逻辑说明content1表示等待 1 秒后跳转到cart.jsp常用于支付模拟成功后自动回到购物车或订单列表。但更好的做法是服务端完成后直接重定向避免浏览器前进后退时重复提交订单。我处理这种场景时会优先在 Servlet 里调用response.sendRedirect(cart.jsp)只有确实需要“先展示结果页再跳转”的场合才用 meta refresh因为前者不会产生二次提交。平时拆这种老项目我最懊恼的不是代码看不懂而是环境问题把时间全耗光。后来给自己定了一条规矩凡是 SQL Server JSP 的老资源动手前必须先排序规则、实例名、TCP 端口三项配置再考虑业务代码。从那以后我每次拿到旧技术栈项目都强制走一遍“库表字段 → 连接串 → 关键页面”顺序省掉大半折腾时间。希望这些过程记录能帮你把这条路走得更顺一点。本文还有配套的精品资源点击获取
返回列表