尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java+SQL Server 2000超市管理系统源码跑通指南

Java+SQL Server 2000超市管理系统源码跑通指南 简介这是一份基于Java与SQL Server 2000开发的超市管理系统完整源代码包适合Java初学者、课程设计者以及需要了解桌面数据库管理系统架构的开发者。系统涵盖库存、销售、客户、员工等典型业务模块代码中涉及Swing图形界面、JDBC数据库交互、MVC分层设计、异常处理及多线程等关键知识点并配有数据库脚本便于直接运行和二次开发。资源包共218个文件压缩后约2.76MB主要包含63个class编译文件、18个java源文件、相关SQL脚本、HTML页面、JAR包以及各类工程辅助文件可对照源码与编译产物梳理完整项目结构。已有551人学习下载。包内提供的源码与数据库脚本能帮助读者理解Java桌面开发与SQL Server 2000的衔接方式同时参考其中的窗口管理和业务处理逻辑可作为课程设计或毕业设计的实用素材。1. 老古董Java超市系统为什么还有人要它一看到「JavaSQL Server 2000超市管理系统源代码.zip」这种标题第一反应往往是2000年的技术栈还有人要但这类老源码在课程设计、毕业设计、旧系统二次开发里恰恰是刚需不少同学拿到的就是一份带数据库备份和Swing界面的完整项目。它的价值不在技术新而在结构全JDBC连接、DAO层、业务层、事件驱动的界面层一应俱全看懂它等于把Java基础过了一遍。下面按实际操作顺序把数据库还原、驱动配置、代码骨架、避坑记录和验证方法一次讲完目标是让你照着走一遍就能跑起来。2. 还原SQL Server 2000数据库决定成败的第一步拿到zip后别急着打开IDE先解压找数据库文件。这类源码通常会在压缩包里带一个db或database目录里面是.bak备份文件或分离出来的.mdf/.ldf文件对。这两种形态处理方式完全不同bak要走还原mdf/ldf要附加。SQL Server 2000的备份和之后的版本兼容性一般用新版SSMS还原时经常报“cannot open backup device”或“媒体集有误”所以先确认目标机器的SQL Server版本和文件格式比上来就执行命令要重要。2.1 先分清数据库文件是备份还是附加bak与mdf处理方式不同先看文件后缀。带.bak的是备份文件对应“还原”操作带.mdf和.ldf的是数据文件和日志文件对应“附加”操作。这套判断决定命令写法还原要指定逻辑名和物理路径附加只需要给文件路径。老资料里经常出现把.mdf直接当bak文件处理结果报错的情况所以拿到文件先看扩展名别猜。区分逻辑其实很简单备份文件是数据库的“快照日志切片”里面打包了文件路径信息数据文件本身是数据库正在用的文件。还原的过程相当于把快照重新铺到新环境附加的过程相当于告诉实例“这个文件已经是一个数据库了直接挂进来”。SQL Server 2000时代还没有“文件流”概念路径和实例绑定很死所以换机器后还原更容易踩路径冲突。2.2 还原命令的两种写法与WITH MOVE参数如果你拿到的是bak文件常见做法是在查询分析器里执行RESTORE命令。SQL Server 2000的查询分析器对中文环境支持一般但命令本身很稳定。示例RESTORE DATABASE SuperMarket FROM DISK D:\backup\SuperMarket.bak WITH MOVE SuperMarket_Data TO D:\MSSQL\data\SuperMarket_Data.mdf, MOVE SuperMarket_Log TO D:\MSSQL\log\SuperMarket_Log.ldf, REPLACE这里关键是WITH MOVEbak文件里记录的是原来那台机器的物理路径目标机器上目录结构不一样不重新指定就会报“无法打开物理文件”或“文件已存在”。MOVE后面的第一个参数是逻辑文件名可以在还原前用RESTORE FILELISTONLY查一下第二个TO是目标机器上的实际路径目录必须提前建好。REPLACE表示如果目标库已存在就覆盖重来适合反复导入的场景但生产库慎用。如果拿到的是mdf/ldf文件对直接附加sp_attach_db SuperMarket, D:\MSSQL\data\SuperMarket_Data.mdf, D:\MSSQL\log\SuperMarket_Log.ldfsp_attach_db是SQL Server 2000自带的系统存储过程参数就是要附加的库名和文件路径序列。注意日志文件可以缺省但缺省后系统会自动生成日志数据文件损坏时可以用这种方式抢救但事务日志会丢之后运行如果出现LSN不一致报错还原回来再附加是唯一解。附加成功后会立刻出现在企业管理器的数据库列表里不需要再导入数据。2.3 还原后立刻验证表清单与关键表字段数据库挂上后别急着跑程序先在查询分析器里跑一遍表清单确认不空SELECT name FROM sysobjects WHERE xtype USQL Server 2000没有sys.tables用sysobjects是正统方式。跑出来清单后重点看三类表用户/员工表、商品/库存表、销售/明细表。管理系统只要这三类表里有记录说明备份内容完整如果查询结果为空但库已还原大概率是附加错了文件或文件的逻辑名对不上。然后再看一眼排序规则中文环境常见的是Chinese_PRC_CI_AS。老系统在GBK环境下建的库编码默认GBK这直接关系到后面JDBC连接时的编码参数。这一步不做后期中文乱码会牵扯出一整串问题。到此数据库就绪下一步要处理的是Java这一侧驱动、连接配置和启动顺序。3. 用JDBC连上2000年的SQL Server配置改法与启动顺序数据库还原完成只是第一步真正让“数据库存在”变成“程序能用”靠的是驱动和连接字符串。SQL Server 2000那个年代的JDBC驱动主流是微软官方驱动和开源的JTDS。老源码包大概率会把jar放在项目根目录的lib下启动前先看lib目录是不是空的这一步比改代码还关键。3.1 驱动选择微软旧驱动和JTDS的取舍经常见到的驱动写法有这么两组对应不同的类名和URL微软旧驱动sqljdbc.jarclassic版Class.forName(com.microsoft.jdbc.sqlserver.SQLServerDriver); // URL: jdbc:microsoft:sqlserver://localhost:1433;DatabaseNameSuperMarketJTDSjtds开源版Class.forName(net.sourceforge.jtds.jdbc.Driver); // URL: jdbc:jtds:sqlserver://localhost:1433/SuperMarket两种写法在SQL Server 2000下都能跑但JTDS在处理老版本TDS协议、整数类型映射和连接池兼容性上更省心。微软旧驱动在新版JDK下容易因模块化机制加载失败而JTDS对JDK 8依然友好。如果源码包两个jar都有优先用JTDS并把老代码里的驱动类和URL全部替换成JTDS格式。别小看这个替换绕过了后面一大半的奇怪报错。3.2 数据库连接类的标准写法与db.properties不管源码包用哪种方案连接管理类的结构都差不多一个DbUtil类负责加载驱动、拿连接、关闭连接。常见写法如下public class DbUtil { private static final String DRIVER net.sourceforge.jtds.jdbc.Driver; private static final String URL jdbc:jtds:sqlserver://localhost:1433/supermarket; private static final String USER sa; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException e) {} } if (stmt ! null) { try { stmt.close(); } catch (SQLException e) {} } if (conn ! null) { try { conn.close(); } catch (SQLException e) {} } } }Class.forName的作用是让驱动类在静态代码块里把自己注册到DriverManager这一步在JDBC 4.0之后可以省略但2000年写的代码习惯沿用这个写法保留它没有坏处。关闭连接的顺序必须是rs→stmt→conn反着关在连接复用时会报“ResultSet已关闭”或“连接已被占用”。如果压缩包里的db.properties文件已经存在把上面的四个常量改成从Properties读取项目迁移到别的数据库时只需改文件不需要改代码这个改造我在第6章会再展开。3.3 启动顺序先服务端后客户端别反着来跑老项目时最常见的一个错误顺序是先在IDE里启动客户端再想起来数据库没起然后报错后反过头来找数据库。正确顺序应该是启动SQL Server 2000服务开始菜单里的SQL Server服务管理器选实例后点Start如果源码包里有Server入口类类名里常带Server字样的main方法先启动它它负责网络监听最后启动LoginUI/MainUI这类带界面的入口类另外命令行或IDE里要确认Java环境变量配置是对的JAVA_HOME指向本机安装的JDKPATH里包含%JAVA_HOME%\binCLASSPATH不要写死用通配符更省事。这一步常常被忽略但老项目对CLASSPATH的敏感度比新项目高得多漏掉一个lib目录就起不来。有个判断技巧先看有没有Server入口类。如果有一个带Server字样且main方法里在等待Socket连接的类说明这是经典的客户端服务端模式没有的话客户端直接连SQL Server。确认模式后启动顺序就很清晰了。服务端没起来但客户端先跑常见的结果是抛“连接失败”然后你在这台机器上排查半天最后发现只是顺序反了。4. 读懂这套代码的骨架从登录到结账的关键调用链跑起来之后别满足于“看到窗口”。这套系统的核心价值是三层结构——界面层、业务层、数据访问层。读代码时从登录界面一步步跟到数据库你就掌握了一次完整的Java课程设计案例源码的解读方法。读懂调用链之后改功能、加报表、修bug才有下手点。4.1 三层结构一眼认出来从包名到类名大多数JavaSwing课程设计源码的包结构长这样src/ com/supermarket/ db/DbUtil.java model/User.java model/Product.java model/Sale.java dao/UserDao.java dao/ProductDao.java dao/SaleDao.java ui/LoginUI.java ui/MainFrame.java ui/SaleUI.javaui包处理鼠标事件和窗口切换dao包直接写SQLmodel只是数据载体。老课程设计常常把业务逻辑直接塞在ui里service包经常是空的这是判断代码质量的一个关键信号。如果你的压缩包里没有service包别惊讶改的时候自己补上就行。识别出这层结构后新增功能时就知道该动哪个文件不会到处乱翻。4.2 登录到结账一次完整的数据流我从登录开始讲一次数据流命名按常见版本示意实际类名可能不同但方向一致LoginUI的登录按钮拼装User对象调用UserDao.authenticate(username, password)UserDao内部执行SELECT语句返回找到的UserLoginUI判断结果后setVisible切换主界面。结账流程类似SaleUI收集购物车SaleDao.insertSale(sale, items)方法里先插入销售主表再循环插入明细表最后更新库存。下面用JDK 5的泛型写法示意老JDK 1.4里把ListSaleItem换成Vector即可逻辑不变。这里最值得读的是insertSale这个方法的JDBC写法public boolean insertSale(Sale sale, ListSaleItem items) { Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DbUtil.getConnection(); conn.setAutoCommit(false); // 事务从这里开始 ps conn.prepareStatement( INSERT INTO sale(no, operator, total, sale_date) VALUES (?, ?, ?, getdate()), Statement.RETURN_GENERATED_KEYS); ps.setString(1, sale.getNo()); ps.setString(2, sale.getOperator()); ps.setBigDecimal(3, sale.getTotal()); ps.executeUpdate(); rs ps.getGeneratedKeys(); int saleId -1; if (rs.next()) { saleId rs.getInt(1); } for (SaleItem item : items) { ps conn.prepareStatement( INSERT INTO sale_item(sale_id, product_id, qty, price) VALUES (?, ?, ?, ?)); ps.setInt(1, saleId); ps.setInt(2, item.getProductId()); ps.setInt(3, item.getQty()); ps.setBigDecimal(4, item.getPrice()); ps.executeUpdate(); ps conn.prepareStatement( UPDATE product SET stock stock - ? WHERE product_id ?); ps.setInt(1, item.getQty()); ps.setInt(2, item.getProductId()); ps.executeUpdate(); } conn.commit(); // 全部成功才提交 return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) {} } e.printStackTrace(); return false; } finally { DbUtil.close(conn, ps, rs); } }这段代码里有两个地方值得细看。第一个是setAutoCommit(false)insert主表、insert明细、update库存这三条SQL要么全部成功要么全部失败这就是Java保证数据一致性的最基础实现也是在老项目里最容易改出问题的地方。第二个是PreparedStatement的参数绑定老代码里如果看到字符串拼接SQL建议全部改成这种写法一方面防转义注入另一方面类型转换交给驱动而不是手写引号。注意SQL Server 2000的datetime列用getdate()生成在Java侧用setTimestamp也能写但getdate()更省事也避免时区换算出问题。注意部分老驱动对getGeneratedKeys支持不稳定拿不到自增主键时改为INSERT执行后立刻执行SELECT SCOPE_IDENTITY()用结果集的第一个int作为saleId。4.3 改代码时的两个关键约束事务边界与金额计算读完调用链后想动手改老代码先记住两个容易让你后悔的约束。事务边界任何“先写主表再写子表”“先扣库存再记流水”的操作都必须包在一个Connection里事务边界就是那条Connection的生命周期。很多老项目翻车的原因是每执行一句SQL就new一个Connection第二句失败后第一句已经提交库存与销售额对不上。接手这类代码时先把散落的语句收进同一个方法再加事务。金额计算是第二个约束老代码里经常出现double类型存储金额结账一旦涉及折扣、小数double的浮点误差在期末对账时就会现形。改动建议是把金额、单价、折扣率全部换成BigDecimalSQL侧对应字段用decimal而不是float。这属于Java基础里的老生常谈可在这种课程设计源码里几乎每份都犯你改掉一个就相当于帮项目补了一个大洞。这两个约束只花半天时间理顺后续改代码不会越改越乱。5. 历史遗留项目跑通避坑记录五个典型故障与排查顺序即使数据库还原成功、代码也能编译真正跑起来还会遇到一堆环境问题。这些问题看起来随机实际上都有固定的触发条件按下面顺序排查能省很多时间。排查顺序的总体原则是先依赖再网络再编码再连接最后看JDK版本。从后往前倒着查很容易陷入“日志一直在报同一个错”的循环。5.1 第1个坑ClassNotFoundException驱动jar没进lib现象启动即抛java.lang.ClassNotFoundException: com.microsoft.jdbc.sqlserver.SQLServerDriver或net.sourceforge.jtds.jdbc.Driver。原因驱动jar不在classpath。IDE导入源码时经常漏掉lib目录或者lib目录里的jar没有被选为依赖命令行运行时则是CLASSPATH没写对。解决先在lib目录确认jar存在且名字拼写正确再在IDE里把它Add to Build Path。命令行运行时用通配符别手写全路径java -cp lib/*;out/production/SuperMarket com.supermarket.ui.LoginUI注意Windows下分隔符是分号Linux/macOS是冒号。分号写错下一个异常还是ClassNotFoundException。这类依赖问题说穿了很简单但不少同学卡在这一步一整晚。5.2 第2个坑1433端口连不上实例名和网络服务的问题现象抛SQLException或Connection refused程序卡在连接上迟迟不返回。原因SQL Server 2000默认实例监听1433端口但命名实例默认是动态端口另一个常见原因是服务没启动或防火墙拦了1433。解决先确认服务管理器里实例状态是Running再在命令提示符下执行telnet 127.0.0.1 1433能连通说明服务和端口都正常。连不通就检查企业管理器里的服务器网络实用工具启用TCP/IP并把端口固定为1433。命名实例的URL要带上instance参数JTDS格式是jdbc:jtds:sqlserver://localhost:1433/supermarket;instance你的实例名。注意这里数据库名大小写不影响连接SQL Server不区分大小写但实例名是区分大小写的。5.3 第3个坑中文乱码GBK与UTF8对不上现象程序界面中文显示正常但读出来的是乱码或者反过来界面显示乱码数据库里正常。原因整条链路编码不一致。SQL Server 2000库多数是GBK体系的Chinese_PRC_CI_AS排序规则源码文件可能是GBK而JVM的默认编码在中英文系统上表现不同JDK 18以后默认强制UTF-8。换机器后链路错位乱码就现形。解决如果程序本身按GBK编写在IDE的VM options或命令行加-Dfile.encodingGBKSQL语句里出现的字符串改为Nxxx方式强制按Unicode处理。注意这不是玄学是编码链路的对齐问题。老系统没有统一编码设计能跑通说明数据库和JVM恰好对齐了一旦换机器先查这一条。乱码问题排查起来很容易绕远先从数据库排序规则和JVM启动参数两头发比逐个表改字段快得多。5.4 第4个坑连接空闲后被回收程序放一会儿再用就报错现象程序刚启动没问题隔一段时间常见是几十分钟再操作就抛“对象已关闭”或“连接已重置”。原因连接长时间空闲后数据库端或网络链路会将其关闭程序里长期持有Connection而不释放空闲超时一到连接就失效了。这不是代码里的业务bug而是连接生命周期管理问题。解决每次操作用完就在finally里从ResultSet到Statement再到Connection依次close不要让连接长期持有。老代码里如果看到成员变量里保存Connection然后到处复用的改成方法内局部变量。这个习惯比加连接池配置更基础先修这个再考虑池化。连接池不是万能药池里的空闲连接同样会被数据库回收池化之后还要配置空闲检测参数。5.5 第5个坑JDK版本过高UnsupportedClassVersionError现象启动即报UnsupportedClassVersionError提示类文件版本号与运行时版本不一致。原因源码包编译时用的JDK版本较低本机却装了JDK 11或17。老代码很多是JDK 1.4/1.5时期写的较新JDK运行时会直接拒绝加载。解决装一个JDK 8并在IDE里把项目SDK和编译级别都切到JDK 8。装了之后别忘了JAVA_HOME环境变量配置要同步指向新装的JDKPATH里不能还残留高版本JDK的路径。这里有个很常见的翻车现场命令行java -version看到的是新版本IDE里却用老SDK编译两者不一致导致“能编不能跑”。用java -version确认当前版本再看IDE里Project Structure命令行、项目SDK、编译级别三个地方一致才稳。这类问题的排查顺序也常被Java面试题问到本质就是环境变量、驱动加载、JDK版本这几个固定原因先环境后代码最省时间。如果再遇到组合故障驱动没加载、端口不通、编码不对一起出现按这里的顺序重头过一遍通常比盯着一个点看更快。6. 用最小用例验证系统两种检查思路和一个改造技巧跑起来不等于能用。想确认这套源码真的可用我习惯用两个最小用例只花十分钟但能覆盖主链路。这两个用例只走最核心的路径登录和结账。数据库操作类项目里这两条路径能把连接、查询、事务、更新全部串起来。6.1 两个最小用例登录后开单与盘点报表用例一是登录后开一张销售单输入商品、数量结账然后去数据库查库存和销售汇总。手工算一下销售金额跟界面结账显示的值做对比。一次能对上说明JDBC读写和事务提交都在正常工作。SELECT product_id, stock FROM product WHERE product_id 1; SELECT SUM(amount) AS total FROM sale WHERE sale_date 2025-01-01;用例二是盘点报表查询报表模块拉出来的数据跟上面SQL查出来的数字逐项核对。注意订单量大的库还有金额字段类型转换的坑如果报表显示的是浮点数而数据库里是decimal看到0.9999这种数字别奇怪先检查取数代码里的类型映射。两个用例都通过这套系统才算真正跑到你自己手里了。6.2 一个最小改造把连接信息抽到配置文件如果确认主链路能跑通我建议你顺手做一个最小的改造把DbUtil里写死的连接信息抽出去。在src目录建db.propertiesjdbc.drivernet.sourceforge.jtds.jdbc.Driver jdbc.urljdbc:jtds:sqlserver://localhost:1433/supermarket jdbc.usersa jdbc.password123456然后DbUtil里改成Properties加载只贴核心片段Properties props new Properties(); try (InputStream in DbUtil.class.getClassLoader().getResourceAsStream(db.properties)) { props.load(in); } Class.forName(props.getProperty(jdbc.driver)); String url props.getProperty(jdbc.url);提示这个片段用了JDK 7的try-with-resources如果编译级别低于JDK 7把它改回try/finally在finally里关闭InputStream。这样以后再迁到别的机器或换数据库实例只动db.properties一个文件不用再碰编译产物。这个改造本身不难但它让整套源码具备了交付能力——别人拿到手只需要改一个文件配置就能跑。数据库文件、依赖jar、配置文件三者分开管理是接手老项目时最值得养成的习惯。我的习惯是先验证主链路再改代码改之前先跑通最小用例每改一步都回到用例上验证这样出了问题能第一时间定位是数据库侧还是Java侧。老项目跑通流程比读懂每一行更重要先把“登录→开单→结账→对账”这条链走顺再回头细读代码、做改造也不迟。希望帮到你。本文还有配套的精品资源点击获取
返回列表