
简介一份基于JSPMSSQL的Java进销存管理系统毕业设计资源适合需要完成课程设计或毕业设计的计算机专业学生及Java Web入门者。资源共163个文件包含105个class字节码、31个java源码、4个jar依赖库以及png界面截图、doc毕业设计文档、xls报表和mdf/ldf数据库文件zip压缩包仅1.7MB。系统采用三层架构涵盖商品管理、供应商管理、进货管理、销售管理、库存管理和报表分析等核心模块源代码中可学习JSP页面与Servlet、JavaBean的业务联动以及通过JDBC操作MSSQL数据库的完整写法。目前已有249人学习对于希望快速搭建进销存项目、理解Java Web开发全流程的读者来说是一份可直接参考的完整工程。1. 一份能跑的 JSP MSSQL 进销存毕设先看懂它再谈部署如果你正在做 java 进销存管理系统 这类毕业设计手里刚拿到一个 “jspmssql 包含源代码毕设.zip” 的压缩包最怕的不是代码看不懂而是打开之后不知道从哪下手。我拆过不少这类毕设资源绝大多数是 JSP Servlet SQL Server 2008 的组合里面通常有一份数据库脚本、一个 Web 项目和一个写死的数据库连接工具类。这套组合能解决的是进销存最核心的业务闭环商品入库、销售出库、库存查询和基础的用户权限管理适合 java 基础一般、想在短时间内把毕设跑起来再改出自己东西的人。先说结论这包东西能不能用取决于你能不能在两小时内把数据库附加成功、把登录页跳过去本篇文章就按这个路径帮你拆。2. 拆开压缩包目录结构、数据库脚本与登录链路2.1 目录结构先看什么src、WebRoot、lib 各装什么打开压缩包之后先别急着往 Tomcat 里扔。我一般会先看三个地方src 目录下有没有DBHelper.java或DBUtil.javaWebRoot/WEB-INF 下有没有web.xmllib 目录下有没有sqljdbc4.jar或者sqljdbc.jar。这三点决定你能否在十分钟内把数据库连上。这类毕设项目的典型分层是这样的src下放着 Servlet 和工具类WebRoot下放着 JSP 页面和静态资源WEB-INF/lib放 JDBC 驱动。如果你发现lib目录是空的那就要小心了——很多打包上传的毕设资源会漏掉驱动你得自己去微软官网下载对应 SQL Server 2008 的sqljdbc4.jar否则 Tomcat 启动后一访问数据库就报ClassNotFoundException。另一个高频问题是web.xml里配置了 Servlet 映射但.java文件没编译成.class。如果压缩包只给了源码你需要用 Eclipse 或 IDEA 把它导入后重新编译部署。很多新手卡在这一步项目文件看着挺全但 Tomcat 一启动就 404就是因为 WebRoot 下只有.java没有.class。2.2 数据库脚本建表、初始化数据与 mssql 连接字符串数据库脚本通常是database.sql或jcxs.sql这类名字里面包含建库、建表和初始化数据。我用 SQL Server Management StudioSSMS打开脚本后会先检查三件事数据库名、排序规则、初始数据。SQL Server 2008 下连接字符串的常见写法是jdbc:sqlserver://localhost:1433;DatabaseNamejcxs如果脚本里建库的IF NOT EXISTS判断写得不够严谨重复执行时会报错。我习惯的做法是先手动创建一个空数据库再单独执行脚本中的建表语句和插入语句这样能避免“数据库已存在”这类干扰。注意脚本中如果使用了nvarchar和Chinese_PRC_CI_AS排序规则那说明作者考虑过中文字段存储的问题。如果你的脚本里全是varchar那后面查中文数据时大概率会出现乱码或查询不到的诡异现象我建议在建表后手动把字符类型改成nvarchar这在第 5 章会展开讲。2.3 从登录到主界面Session 校验、权限过滤器的落地写法进销存系统的登录逻辑是所有功能模块的入口也是你判断这份源码质量的最好样例。常见写法是LoginServlet接收用户名和密码用PreparedStatement查询用户表比对通过后把用户信息放进Session然后response.sendRedirect(index.jsp)如果失败则forward回login.jsp并带一个错误提示。// LoginServlet.java 核心代码 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); // 必须放在读取参数之前 String username request.getParameter(username); String password request.getParameter(password); String sql SELECT * FROM tb_user WHERE user_name? AND user_pwd?; try (Connection conn DBHelper.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { request.getSession().setAttribute(loginUser, username); response.sendRedirect(index.jsp); } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } } catch (Exception e) { e.printStackTrace(); // 正式环境建议换成日志输出 } }这段代码有两个关键点第一setCharacterEncoding(UTF-8)必须在任何getParameter调用之前执行否则中文参数进来就乱码第二PreparedStatement的参数占位符从 1 开始编号setString(1, username)对应第一个?顺序不能反。如果你发现源码里用的Statement拼接字符串查询那建议你改成PreparedStatement这既是毕设答辩时老师喜欢问的点也是避免 SQL 注入的基本操作。这类项目里登录之后的主界面通常是一个左右框架布局左侧是功能菜单商品管理、入库管理、出库管理、库存查询右侧是欢迎页或统计仪表盘菜单对应的 URL 都在left.jsp里用a标签指向不同 Servlet你顺着这个链路就能摸清整个项目的请求流。3. 从 0 到 1 部署JDK、Tomcat 与 SQL Server 2008 的启动全过程3.1 环境选型JDK 1.7/1.8 Tomcat 7/8 的兼容组合这类老项目对运行环境是有脾气的。我跑过的 JSP MSSQL 毕设项目里最常见的组合是 JDK 1.7 或 1.8 搭配 Tomcat 7 或 8数据库是 SQL Server 2008 R2。如果是 JDK 1.8 Tomcat 9部分代码里用了 JDBC 驱动旧 API 的话会报错所以我会先按老环境配置复现。先说 java 环境变量配置 这个绕不开的坎。配环境变量时JAVA_HOME指向 JDK 安装根目录PATH里加上%JAVA_HOME%\binCLASSPATH建议直接不配——Java 5 之后CLASSPATH已经不需要手动设置反而容易引入rt.jar之类的高版本机制导致冲突。配置完在命令行输入java -version验证看到版本号和安装一致就行。Tomcat 版本要根据 JDK 匹配JDK 1.7 配 Tomcat 7JDK 1.8 配 Tomcat 8 或者 9 都行。如果启动后页面报 500 且日志里出现UnsupportedClassVersionError那就是 Tomcat 和 JDK 版本不匹配属于最高频的 java 启动失败怎么解决 场景之一。3.2 SQL Server 配置混合认证、TCP/IP 协议与 JDBC 驱动SQL Server 2008 默认可能没开启 TCP/IP 协议而 JDBC 连接必须走这个协议。安装完 SQL Server 后打开“SQL Server 配置管理器”找到“SQL Server 网络配置”下的“MSSQLSERVER 的协议”把 TCP/IP 状态从“禁用”改成“启用”然后重启 SQL Server 服务。接着设置混合认证模式这步很关键。在 SSMS 中右键服务器属性在“安全性”里选择“SQL Server 和 Windows 身份验证模式”否则你代码里写sa账号登录会报“用户 sa 登录失败”。我一般在测试环境直接给sa设一个简单密码比如123456配合连接串jdbc:sqlserver://localhost:1433;DatabaseNamejcxs验证连通性。JDBC 驱动要放进WEB-INF/lib目录注意 SQL Server 2008 对应的是sqljdbc4.jar支持 JDBC 4.0不要用针对老版 SQL Server 2000 的sqljdbc.jar。判断驱动是否加载成功可以看 Tomcat 日志里有没有ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver如果有就是 jar 没放对位置或者没重启。3.3 部署步骤导入项目、附加数据库、启动联调我用 Eclipse 导这类项目的标准流程是这样新建一个 Dynamic Web Project把源码的src和WebRoot内容直接复制进对应目录然后把sqljdbc4.jar放进WEB-INF/lib最后用 Eclipse 自带的 Tomcat 插件启动。如果是已编译好的项目直接打成 war 包扔到 Tomcat 的webapps目录下启动时自动解压。数据库恢复我推荐“附加数据库”而不是“导入 .bak”。右键数据库节点选择“附加”指向压缩包里的.mdf文件如果Log文件缺失先在附加窗口里移除.ldf让它自动重建日志否则会报“无法为数据库分配日志文件空间”。这是个非常坑的细节很多人卡在这一步以为数据库文件损坏了。启动完成后浏览器访问http://localhost:8080/项目名/login.jsp如果登录页能出来说明 JSP 引擎正常输入账号密码后能跳转到主界面说明 JDBC 连接和数据库都通了这时候整个项目就是可以用的状态。3.4 常见报错对照速查表现象可能原因处理方式404 找不到页面war 包没解压成功或项目名拼错检查webapps下是否存在项目目录500 ClassNotFoundExceptionJDBC 驱动缺失把sqljdbc4.jar放进WEB-INF/libUnsupportedClassVersionErrorJDK 与 Tomcat 版本不匹配JDK 1.8 配 Tomcat 8用户 sa 登录失败SQL Server 未开启混合认证服务器属性 → 安全性 → 混合模式连接超时TCP/IP 协议未启用或 1433 端口未开配置管理器启用 TCP/IP防火墙放行 1433数据表不存在脚本没执行成功或数据库名不一致核对DatabaseName是否与建表库一致4. 核心业务拆解入库、销售出库与库存预警的代码实现4.1 入库单提交事务回滚与库存表联动进销存系统的核心不是页面多好看而是库存数据准不准。入库单提交的常见逻辑是前台表单提交商品编号、数量、单价Servlet 接收后先插入入库明细表再更新商品库存表的库存数量。这两步必须放在同一个数据库事务里否则会出现“入库单有了但库存没加”的脏数据。// StockInServlet.java 事务控制核心代码 Connection conn null; try { conn DBHelper.getConnection(); conn.setAutoCommit(false); // 关闭自动提交开始手动事务 String insertDetail INSERT INTO tb_stock_in (goods_id, quantity, price, in_date, operator) VALUES (?, ?, ?, GETDATE(), ?); PreparedStatement ps1 conn.prepareStatement(insertDetail); ps1.setInt(1, goodsId); ps1.setInt(2, quantity); ps1.setBigDecimal(3, price); ps1.setString(4, operator); ps1.executeUpdate(); String updateStock UPDATE tb_goods SET stock stock ? WHERE id ?; PreparedStatement ps2 conn.prepareStatement(updateStock); ps2.setInt(1, quantity); ps2.setInt(2, goodsId); ps2.executeUpdate(); conn.commit(); // 两个操作都成功才提交 } catch (Exception e) { if (conn ! null) { conn.rollback(); // 任何一步失败就回滚库存和单据不会不一致 } e.printStackTrace(); } finally { if (conn ! null) { conn.setAutoCommit(true); // 恢复默认避免连接池复用受影响 conn.close(); } }这段代码里setAutoCommit(false)是事务的开关commit()和rollback()是事务的两个出口。新手常犯的错误是把commit()放在try里但漏掉rollback()或者忘了在finally里恢复setAutoCommit(true)——后者在连接池环境下会让后续请求一直处于事务中导致锁表、查询卡死。GETDATE()是 SQL Server 获取当前时间的函数比在 Java 里拼字符串传new Date()更可靠也避开了日期格式的时区问题。如果你的 SQL Server 和 Tomcat 不在同一台机器上用数据库时间还能保持时间源一致。4.2 销售出库扣减库存、金额计算与负库存拦截销售出库的逻辑和入库相反是扣减库存、计算销售金额并写销售记录。比入库多一层校验库存不能为负。常见做法是先查当前库存再和要卖的数量比较。但这里有个竞态条件的坑——两个用户同时下单时先查后改的逻辑可能让两个请求都通过校验库存被扣成负数。我处理这个问题有两种做法一是用UPDATE tb_goods SET stock stock - ? WHERE id ? AND stock ?在 SQL 里一次性完成扣减和校验executeUpdate()返回 0 行就说明库存不足二是把“查库存”和“扣库存”放进同一个事务并配合行锁SELECT ... WITH (UPDLOCK)。对于毕设项目来说第一种写法就够用而且答辩时更容易解释清楚。销售金额的计算建议用BigDecimal而不是double。SQL Server 的money类型或decimal(10,2)配合BigDecimal才能避免 0.1 0.2 这类浮点误差这在电商类项目里是必问的面试点。入库单价和销售单价分开存报表里才能算毛利别偷懒只存一个金额字段。4.3 库存查询与预警SQL 聚合、多表 join 与去重库存查询是这类项目的门面功能通常要展示商品名称、供应商、当前库存、预警状态。一次典型的多表查询会 jointb_goods、tb_supplier、tb_stock_in三张表这里最容易踩的坑就是 join 出来的结果集重复——因为一个商品有多条入库记录join 后商品基础信息会重复出现这就是 mssql 去重 多表查询 的应用场景。-- 商品维度的库存汇总避免多条入库记录导致重复行 SELECT g.goods_code, g.goods_name, s.supplier_name, g.stock, g.low_stock, CASE WHEN g.stock g.low_stock THEN 预警 ELSE 正常 END AS stock_status FROM tb_goods g INNER JOIN tb_supplier s ON g.supplier_id s.id WHERE g.stock g.low_stock ORDER BY g.goods_code;这个查询里INNER JOIN只取有供应商匹配的商品CASE WHEN根据stock和low_stock生成预警状态列。如果商品资料里设置了安全库存阈值就可以用同样的条件生成库存预警列表。注意low_stock这个字段在入库出库时不会被自动更新它是商品资料模块里单独维护的这个边界要想清楚。如果要汇总一段时间内的入库和出库总量可以用SUM(CASE WHEN ... THEN quantity ELSE 0 END)配合GROUP BY做行转列聚合。这样做的好处是只扫一遍表比子查询嵌套性能好得多。SQL Server 2008 不支持ROW_NUMBER() OVER (PARTITION BY ...)配合删除不2008 是支持的这是 2005 就有的功能。如果你在 2008 里用不出ROW_NUMBER检查一下数据库兼容级别是否被设成了老版本。5. 避坑与排查乱码、超时与日志分析的实战记录5.1 登录页能打开但一提交就报 500现象输入账号密码后页面直接跳转到 Tomcat 默认的 500 错误页控制台输出Cannot create PoolableConnectionFactory或Unknown database。原因绝大多数情况是DBHelper.java里的连接串写死了某个数据库名或密码和本机 SQL Server 实际配置不一致也有可能是sqljdbc4.jar没有被 Tomcat 加载。解决先手动用 SSMS 拿同样的账密连接一次数据库确认账密和数据库名正确再打开DBHelper.java逐项比对连接串。连接串格式务必是jdbc:sqlserver://localhost:1433;DatabaseNamejcxs不要把DatabaseName写成database或dbname这是 SQL Server 驱动和老式 MySQL 驱动写法混用时的典型翻车点。5.2 插入的中文全变成问号现象页面上录入商品名称“洗发水”存进 SQL Server 后变成“”或者从数据库查出来的中文乱成一团。原因三层问题叠加。第一JSP 页面没有设置pageEncodingUTF-8Tomcat 默认用 ISO-8859-1 解码第二Servlet 里没调request.setCharacterEncoding(UTF-8)第三数据表字段是varchar而 SQL Server 中varchar存储中文依赖数据库排序规则。解决JSP 头部改成% page languagejava pageEncodingUTF-8 contentTypetext/html; charsetUTF-8 %Servlet 的doPost第一行执行request.setCharacterEncoding(UTF-8)数据库里所有存中文的字段统一改nvarchar排序规则设为Chinese_PRC_CI_AS。改完这三处重新插入一条数据验证注意旧数据里的乱码不会自动恢复要清空重插。5.3 SQL Server 连接超时程序卡死无响应现象页面偶尔能打开偶尔长时间转圈最后报Connection timed out或The TCP/IP connection to the host has failed。原因SQL Server 默认没有开启 TCP/IP 协议或者 Windows 防火墙拦了 1433 端口另一个可能是数据库连接没有关闭连接池被耗尽。解决打开“SQL Server 配置管理器”启用 TCP/IP 并重启服务在防火墙入站规则里放行 1433 端口。代码层面检查finally块里有没有把Connection、PreparedStatement、ResultSet都关闭ResultSet没关在 MySQL 下不报错但在 SQL Server 下会长时间占住连接。5.4 Tomcat 启动内存溢出项目起不来现象Tomcat 启动到一半报java.lang.OutOfMemoryError: PermGen space或者Java heap space项目无法访问。原因老项目在 JDK 1.7 时代常用PermGen区如果加载的类太多且-XX:MaxPermSize没设置就会溢出。JDK 1.8 之后PermGen被Metaspace取代这类问题会减少但老代码配合新 JVM 时可能因为 CGLIB 代理类过多触发Metaspace溢出。解决在 Tomcat 的catalina.bat中主动设置 JVM 参数set JAVA_OPTS-Xms256m -Xmx512m -XX:MaxPermSize256mJDK 1.7或set JAVA_OPTS-Xms256m -Xmx512m -XX:MaxMetaspaceSize256mJDK 1.8。同时别把sqljdbc4.jar同时放在 Tomcat 的lib目录和应用的WEB-INF/lib下重复加载容易加剧内存问题。5.5 报表查询越跑越慢SQL Server 日志还暴涨现象库存查询和销售报表在数据量几万条时还能接受到了十几万条就明显卡顿同时MSSQL数据库日志文件膨胀到几个 GB。原因查询涉及的关联字段goods_id、supplier_id没有索引每次 join 都全表扫描日志暴涨则是因为数据库恢复模式设置成了FULL而每次大批量操作都会把日志写满。解决给核心关联字段建索引日常的做法是CREATE INDEX idx_stock_log_goods ON tb_stock_log(goods_id);以及给tb_goods的goods_code建唯一索引。日志方面把数据库恢复模式从FULL改成SIMPLE执行一次DBCC SHRINKDATABASE收缩日志文件。分析这段日志时可以先用 SSMS 的“活动监视器”看阻塞会话是谁再决定要不要杀掉阻塞源这个排查顺序能省很多冤枉时间。6. 二次开发技巧参数化配置、报表优化与答辩准备6.1 把写死的连接信息抽到配置外部几乎每个毕设源码都会把数据库连接信息直接写在DBHelper.java里换一台电脑部署就要改源码重编译非常别扭。我拿到这类资源的第一件事就是新建一个jdbc.properties放到 src 根目录把连接串、账号、密码全部挪进去然后用一个静态代码块加载。// DBHelper.java 改造后的连接获取部分 private static String url; private static String username; private static String password; static { Properties props new Properties(); try (InputStream in DBHelper.class.getClassLoader() .getResourceAsStream(jdbc.properties)) { props.load(in); Class.forName(props.getProperty(driver)); url props.getProperty(url); username props.getProperty(username); password props.getProperty(password); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); }对应jdbc.properties内容drivercom.microsoft.sqlserver.jdbc.SQLServerDriver urljdbc:sqlserver://localhost:1433;DatabaseNamejcxs usernamesa password123456改完之后后续换机器部署只需要改一个文件不必再碰 Java 代码。注意getResourceAsStream的路径是相对 classpath 的文件放在src下编译后会自动进 classes 目录如果你用 Eclipse要确保.properties文件被构建进输出目录。6.2 给库存预警加一个定时刷新进销存系统的预警信息如果只在页面加载时查一次数据库里库存变了页面也不会自动更新演示时观感很差。一个不依赖 Quartz 的轻量做法是在 JSP 里用 JavaScript 定时刷新局部区域页面每个 30 秒请求一次库存预警接口返回 JSON 后更新表格。Servlet 端只需把原本查预警列表的 SQL 结果转成 JSON 输出前端用setInterval定时拉取。注意定时刷新别太频繁SQL Server 并发不高时 30 秒是安全值设成 5 秒会在多人同时操作时把数据库打满。6.3 答辩前必做的三件事第一验证数据库脚本能重复执行。在 SSMS 里把整个脚本再跑一遍确认不会因为表已存在而报错。老师如果现场让你新建数据库还原项目这一步最容易被问倒。第二走通一条完整业务链路登录 → 新增商品 → 入库 → 出库 → 查库存预警。每一步的页面跳转和库存数字变化都要能讲清楚逻辑。第三准备好“为什么用事务”这个问题直接用第 4.1 节的两步操作例子解释比背概念更可信。我拆这份资源时的切身体会是这类 JSP MSSQL 毕设的代码质量参差不齐但业务骨架基本都是完整的真正值钱的不是代码本身而是你肯花一个下午把环境跑通、把事务逻辑理清的过程。从那以后我每次拿到新的毕设包都会强制先走一遍“还原数据库 — 跑通登录 — 做一笔入库单”三条流程流程顺了再谈改代码。这套流程同样适用于你手里的这份资源希望帮到你。本文还有配套的精品资源点击获取