☰
Java SSM砂石矿山管理系统:毕业设计实战与避坑指南
2026/10/5 7:24:52 网站建设 项目流程

简介:基于Java的砂石矿山管理系统设计与实现毕业设计资料,面向计算机专业毕业生及SSM框架学习者。系统围绕矿山砂石销售业务,包含登录校验、销售登记(自动计算单价乘以载重总价)、员工信息维护、加班登记与查询、销售账单按公司、车牌、日期或支付状态统计、其他账目收支记录等完整功能模块,并附毕业论文文档,可用于课程设计或论文撰写参考。资源包共885个文件,大小9.79MB,以Java源码、JSP页面、CSS与JS前端资源、SQL脚本及论文文档为主,其中87个Java文件支撑业务逻辑,59个JSP与25个HTML构成页面,118个CSS和232个JS实现界面样式与交互,另含图片素材和数据库初始化脚本,目录结构适合按模块对照学习。目前已有103人浏览学习。通过完整源码、数据库脚本和论文内容,可了解需求分析、数据库设计、SSM整合及前后端协作的完整思路,便于按模块理解部署和二次开发。

1. 砂石矿山管理系统:一个能落地也能毕业的 Java + SSM 选题

提到 Java 毕业设计,十个里有七个是图书管理系统、学生选课。这套基于 Java + SSM 的砂石矿山管理系统,思路更往传统行业靠:砂石场的销售还在用 Excel 记账,公司、车牌、砂石种类、单价、载重全靠手敲,月底对账要翻半天记录。功能都是实际业务:登录不可注册、销售登记(单价×载重自动算总价、日期自动获取)、人员与加班登记、多条件查询汇总、其他账目倒序查询。技术栈 SSM + Bootstrap,配套毕业论文,适合毕设交差,也适合真想给砂石场换套记账方式的人。这篇按我拆这个项目的顺序走:先看工程结构与登录设计,再讲销售登记和查询统计的 SQL 与计算逻辑,最后把最容易翻车的几个点单独列出。新手照步骤能跑通,熟手直接看避坑章。

2. SSM 骨架与登录模块:从 Eclipse 工程结构到预置账号

2.1 .classpath 文件里的项目出身

下载解压后最先看到的是一批 Eclipse 工程文件:.classpath、org.eclipse.wst.common.component、org.eclipse.wst.jsdt.ui.superType.container。别急着删,它们明确告诉你一个信息:项目是在 Eclipse 系 IDE 里手工建的,不是 Maven 工程。.classpath 里记录的是源码目录 src、输出目录 build/classes、JDK 版本,以及 WebRoot/WEB-INF/lib 下的一堆 jar 依赖。

读 .classpath 的实际作用有两个。第一,确认 JDK 版本——我见过不少人本机装的是 JDK 17,项目还是 1.8 语法,一跑就报 UnsupportedClassVersionError。第二,确认依赖管理方式是 lib 目录,导入时直接用 Eclipse 的 Import Existing Projects 就能进,不用等 Maven 下载。静态资源 style.css、bootstrap.min.css 都放在 WebRoot 下,部署到 Tomcat 后直接可访问,不走打包流程。

一个典型的 .classpath 片段长这样:

<classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="output" path="build/classes"/> </classpath>

kind="src" 是 Java 源码根目录,kind="con" 表示容器依赖,这里能看到 JavaSE-1.8 字样,output 是编译输出目录。导入后如果项目报红叉,回头对照这三项:源码目录对不对、JDK 对不对、lib 有没有被引用。

2.2 SSM 四件套:jdbc.properties 与两个 XML 的关键行

SSM 在老 Eclipse 项目里的配置是固定套路:jdbc.properties 放数据库连接,spring-mvc.xml 管 Controller 扫描和视图解析,applicationContext.xml 管 Spring 容器与 MyBatis 集成,mybatis-config.xml 管 mapper 注册。先看 jdbc.properties,这是启动第一关:

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/sand_mine?useUnicode=true&characterEncoding=utf-8 jdbc.username=root jdbc.password=123456

本机是 MySQL 8.x 的话,driver 换成 com.mysql.cj.jdbc.Driver,url 末尾加 serverTimezone=Asia/Shanghai。数据库名 sand_mine 要和初始化脚本里的建库名一致,编码必须带 utf-8,否则中文全变问号。改完这三行,第一关才算过。

spring-mvc.xml 里两个关键配置:

<context:component-scan base-package="com.sand.controller"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean>

base-package 必须覆盖 Controller 所在包,扫不到就是 404。prefix 和 suffix 决定了 Controller 里 return "login" 会去找 /WEB-INF/jsp/login.jsp——JSP 不在这里或目录名不叫 jsp,页面就白屏。很多"登录页打不开"的翻车就出在这一行。

mybatis-config.xml 里容易出问题的是 mapper 注册。手工项目常见做法是逐个列:

<mappers> <mapper resource="com/sand/mapper/UserMapper.xml"/> <mapper resource="com/sand/mapper/SaleMapper.xml"/> <mapper resource="com/sand/mapper/EmployeeMapper.xml"/> </mappers>

每新增一个 Mapper XML 都要来这里注册,漏了会报 Invalid bound statement (not found)。这个报错我答疑时见了几十次,全是注册遗漏或 namespace 写错。

提示:改完任何 XML 配置都要重启 Tomcat,Spring 容器只在启动时加载一次配置,热部署不一定生效。

2.3 登录不可注册:预置账号与密码比对

登录提示"账号不存在或者密码错误",两个场景共用一句话,不暴露账号是否存在,这是安全上的好习惯。系统没有注册入口,账号来自初始化 SQL 预置。配套毕业论文里 ER 图和用例图对应的就是下面这几张表,登录模块对应的就是 t_user 这一张:

CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(64) NOT NULL ); INSERT INTO t_user (username, password) VALUES ('admin', MD5('123456'));

密码存 MD5,登录时把输入转 MD5 再比对,不落明文。答辩时老师追问密码安全,能答出这一句比多说一个功能都加分。Service 层逻辑:

public User login(String username, String password) { String md5pwd = DigestUtils.md5Hex(password); User user = userMapper.findByUsername(username); if (user != null && user.getPassword().equals(md5pwd)) { return user; } return null; }

Controller 拿到 null 统一提示"账号不存在或者密码错误"。session 里放 user 对象,再用一个 Filter 拦截未登录请求。很多课设只做 JSP 页面跳转判断,自己补一个 Filter 工作量不大,答辩能讲的东西多一样。到这里,骨架和入口就算摸清了,下一步是核心业务——销售登记。

3. 销售登记与总价计算:下拉数据源、自动日期与前后端分工

3.1 下拉数据源:固定字典与动态列表

销售登记页有公司、车牌、砂石种类、单价、载重几个核心输入。砂石种类固定四种:粗砂、细沙、粉砂、块石,这种变化极小的数据写死在 JSP 的 select 里也行,但放字典表更规范。公司列表、车牌列表是动态数据,进页面时 Controller 从对应表查出来,塞进 ModelAndView:

@RequestMapping("/sale/add") public ModelAndView addPage() { ModelAndView mv = new ModelAndView("sale/add"); mv.addObject("companyList", companyMapper.findAll()); mv.addObject("plateList", plateMapper.findAll()); return mv; }

JSP 用 JSTL 渲染:

<select name="companyId" class="form-control"> <c:forEach items="${companyList}" var="company"> <option value="${company.id}">${company.name}</option> </c:forEach> </select>

这里有个新手必踩的点:提交的是 companyId,不是公司名称。很多人直接把名称入库,后续按公司查询只能模糊匹配,没法做精确关联。数据库设计时公司表、车牌表各一张,销售表只存外键 id,页面显示 name,这是外键关联的标准分工。车牌表如果和公司有归属关系,还可以加一个 company_id 字段,做"选了公司自动过滤车牌"的联动,这是这个模块可以自己加的一个小升级。

3.2 总价 = 单价 × 载重:前端实时算,后端必须重算

前端要给操作员实时反馈,让单价和载重的 change 事件都绑到同一个计算函数上:

function calcTotal() { var price = parseFloat($("#price").val()) || 0; var weight = parseFloat($("#weight").val()) || 0; $("#totalPrice").val((price * weight).toFixed(2)); } $("#price").change(calcTotal); $("#weight").change(calcTotal);

parseFloat 解析失败会得到 NaN,|| 0 做兜底。toFixed(2) 保留两位小数。但前端计算的职责只是用户体验,保存时后端必须重算,因为请求可以被绕过,前端算出来的数字不可信:

BigDecimal totalPrice = price.multiply(weight); sale.setTotalPrice(totalPrice);

单价、载重、总价都用 BigDecimal,别用 double。double 做乘法会出现 0.1+0.2=0.30000000000000004 这种玄学误差,金额字段用 double 是给自己埋雷。数据库字段类型也对应 DECIMAL(10,2),Java 算完再写进表,两头口径一致。

3.3 日期自动获取:前端默认值加后端兜底

需求里的"日期自动获取",指的是打开登记页时默认填当天,操作员大多数时候不用改。JS 实现:

function today() { var d = new Date(); var month = (d.getMonth() + 1) < 10 ? "0" + (d.getMonth() + 1) : (d.getMonth() + 1); var day = d.getDate() < 10 ? "0" + d.getDate() : d.getDate(); return d.getFullYear() + "-" + month + "-" + day; } $("#saleDate").val(today());

getMonth() 从 0 开始,所以要 +1;月日补零,是因为数据库 DATE 类型更习惯 yyyy-MM-dd 字符串。后端保存时再兜底一次,提交为空就用数据库 CURRENT_DATE。如果字段是 DATETIME 而前端只传日期字符串,时间部分会变成 00:00:00,对纯日期统计影响不大,但要留意下一节说的日期边界问题。

3.4 人员与加班登记:两张表的字段设计

人员登记是标准增删改查:姓名、年龄、本人电话号码、紧急联系人号码。年龄用 INT,手机号用 VARCHAR(20)——电话号码开头可能有 0,将来存座机分机号也不怕,用 INT 会吃掉前导零。加班表存 employee_id 和 overtime_hours,员工表主键是加班表的外键。

加班时长用 DECIMAL(4,1),能存 3.5 小时,别用 INT。现实中"加班三个半小时"很常见,用 INT 要么把单位改成分钟,要么只能四舍五入。删除员工时要注意:如果该员工已有加班记录,直接删会报外键约束错误,要么先删加班记录,要么加一个逻辑删除字段 is_deleted,这个可以在论文里写成"数据完整性设计"的一节。

4. 查询统计:多条件组合 SQL、倒序账目与加班聚合

4.1 销售查询:四个条件任意组合

需求里最有含金量的是查询:"查询柏松房产在 1 月 1 日到 1 月 10 日还未支付的所有订单及总账"、"查询贵A5236 该车的 1 月总账单"。这是典型的多条件组合查询:公司、车牌、日期区间、支付状态,每个条件可能为空,也可能同时生效。

MyBatis 的<where>+<if>就是为这个场景设计的:

<select id="querySales" resultType="com.sand.entity.SaleVO"> SELECT s.*, c.name AS companyName, p.plate_no AS plateNo FROM t_sale s LEFT JOIN t_company c ON s.company_id = c.id LEFT JOIN t_plate p ON s.plate_id = p.id <where> <if test="companyId != null and companyId != ''"> AND s.company_id = #{companyId} </if> <if test="plateId != null and plateId != ''"> AND s.plate_id = #{plateId} </if> <if test="startDate != null and startDate != ''"> AND s.sale_date &gt;= #{startDate} </if> <if test="endDate != null and endDate != ''"> AND s.sale_date &lt;= #{endDate} </if> <if test="payStatus != null and payStatus != ''"> AND s.pay_status = #{payStatus} </if> </where> ORDER BY s.sale_date DESC </select>

要点有三个。<where>会自动去掉第一个条件多余的 AND,所以每个 if 里放心写 AND。XML 里大于小于号必须转义成&gt;和&lt;,这是 XML 语法层面的问题,不转义文件直接解析失败。日期比较传 yyyy-MM-dd 字符串即可,前提是字段是 DATE 类型。

查询条件从页面穿过来时,Controller 里 new 一个 query 对象让 SpringMVC 自动绑定 request 参数,Mapper 方法直接传对象就行。销售记录积累到几百上千条后建议加分页,老项目常用 PageHelper,一个 PageHelper.startPage(pageNum, pageSize) 接在查询前就能生效;手工 SQL 分页就是 LIMIT。毕业设计答辩提一句分页,是个不错的加分点。

4.2 汇总账单:SUM 查询与条件片段复用

需求要"总账",明细列表之外还要合计金额。两个做法:Java 遍历累加,或者 SQL 里 SUM。数据量小都能用,但我推荐 SQL 层面做——明细一旦分页,Java 累加只加当前页,账就错了。

<sql id="saleWhere"> <where> ... 和 querySales 里完全相同的条件 ... </where> </sql> <select id="sumSales" resultType="java.math.BigDecimal"> SELECT COALESCE(SUM(total_price), 0) FROM t_sale s <include refid="saleWhere"/> </select>

<sql>把条件抽出来,两个查询 include 同一片段,保证条件永远一致。COALESCE 处理空结果,没记录时返回 0 而不是 null,页面显示 0.00 比 null 好看。这是 MyBatis<sql>标签最典型的用法,答辩讲这个点能体现对框架的理解不止 CRUD。

注意:前端页面上显示的汇总数字只供查看,写进报表或打印出来的金额必须走后端 SUM 接口,两边口径在验收时对一遍。

4.3 其他账目倒序与加班时长聚合

"其他账目开支查询按照倒序罗列"——SQL 就是 ORDER BY create_time DESC, id DESC。只按 create_time 排,如果多个账目在同一秒入库,顺序不稳定;加 id DESC 做次级排序,保证记录顺序固定,这个细节在答辩讲数据一致性时可以提。

员工加班时长查询是"某个员工某月某日到某月某日的总加班时长":

<select id="sumOvertime" resultType="java.math.BigDecimal"> SELECT COALESCE(SUM(overtime_hours), 0) FROM t_overtime WHERE employee_id = #{employeeId} AND overtime_date &gt;= #{startDate} AND overtime_date &lt;= #{endDate} </select>

startDate 传当月第一天,endDate 传当月最后一天。这里有个隐藏边界:如果 overtime_date 是 DATE 类型,endDate 传 '2025-01-31',<=能包含当天;但如果是 DATETIME 类型,'2025-01-31' 会被转成 '2025-01-31 00:00:00',当晚的数据就丢了。查不到数据先别怀疑 SQL,先看表结构,这是我反复踩过的坑,下一章展开讲。

5. 避坑指南:数据库连接、日期边界与中文乱码的五个现场

这五个问题是我在同类 SSM 项目里见得最多的,前四个几乎每个来问的人都会中一个。每条按现象、原因、解决的顺序写,方便对照排查。

5.1 启动报错:Communications link failure

现象:Tomcat 启动时控制台抛 Communications link failure,或者 Access denied for user 'root'@'localhost',登录页一直打不开。

原因:三选一。MySQL 服务没启动;jdbc.properties 里用户名或密码写错;MySQL 8 没换 cj 驱动导致协议不兼容。

解决:先命令行敲 mysql -uroot -p 验证数据库能连;再核对 jdbc.properties 三行配置;MySQL 8 把 driver 换成 com.mysql.cj.jdbc.Driver,url 末尾加 serverTimezone=Asia/Shanghai。改完重启 Tomcat,不要只刷新页面,配置是启动时读取的。

5.2 下拉列表空白

现象:销售登记页公司下拉框是空的,F12 看请求返回 200,但 select 里没有 option。

原因:Controller 没查列表塞进 Model;JSP 的 c:forEach 写错;或者 JSTL 标签库没引入。前两种最常见。

解决:Controller 里 addObject 两个列表,JSP 顶部写 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>,确认 lib 下有 jstl 和 standard 两个 jar。JSP 编译错误经常被 Tomcat 吞掉只显示 500,要去 catalina 日志里看具体原因,日志里会有 "Attribute companyList invalid" 之类的明确提示。

5.3 日期范围查询漏掉最后一天

现象:查 1 月 1 日到 1 月 10 日,1 月 10 日当天登记的数据查不出来。

原因:日期字段是 DATETIME 类型,'2025-01-10' 被 MySQL 转成 '2025-01-10 00:00:00',当天 00:00:00 之后的记录全部被<=过滤掉。

解决:两个方案。一是查询条件里把 endDate 加一天,用< 下一天而不是<= endDate;二是业务日期字段直接设计成 DATE 类型。我在不止一个项目里被 DATETIME 边界坑过,现在一律先看表结构再写查询条件,这已经成了习惯。

5.4 中文乱码:姓名变问号

现象:登记的中文姓名、公司名保存后变问号,或者页面显示乱码。

原因:三层都可能——JSP 页面编码不是 UTF-8;Tomcat 请求解码不是 UTF-8;数据库连接 url 没带 characterEncoding,或者表本身是 latin1。

解决:JSP 顶部 pageEncoding="UTF-8";web.xml 里配 CharacterEncodingFilter 强制 request 编码;jdbc url 带 useUnicode=true&characterEncoding=utf-8;建表统一 utf8mb4。改完清掉乱码数据重新测试,别在脏数据基础上继续改,越改越乱。

5.5 页面 404 但映射存在

现象:访问 /sale/add 直接 404,Controller 的 @RequestMapping 检查过没问题,Tomcat 也正常启动。

原因:spring-mvc.xml 组件扫描包路径不对,Controller 没被 SpringMVC 容器扫描到;或者 web.xml 里 DispatcherServlet 的 url-pattern 配成了 *.jsp,JSP 请求永远不进 SpringMVC。

解决:核对<context:component-scan>的 base-package 是否包含 Controller 所在包;DispatcherServlet 的 url-pattern 一般配 /,JSP 由 InternalResourceViewResolver 处理。改完重启,看启动日志是否输出了 RequestMappingHandlerMapping 的映射记录,有输出就说明 Controller 注册成功了。

6. 部署走查与对账验收:跑通全流程的最后一公里

6.1 导入与启动步骤

Eclipse 里 Import Existing Projects 选整个目录,确认编译级别和自己本机 JDK 一致(这类 SSM 老项目基本是 1.8),Build Path 不报红。执行初始化 SQL 建库,改 jdbc.properties 三行,右键项目 Run As -> Run on Server 选 Tomcat。访问路径是 http://localhost:8080/项目名/,项目名对应 web.xml 里的 context path,登录账号就是初始化脚本里预置的 admin/123456。

登录后第一件事不是录数据,而是把每个菜单点一遍,确认页面能开、下拉有数据。然后再按下面的清单走功能验收。

6.2 功能验收清单

模块操作预期结果
登录输入 admin/123456进入首页
登录输入错误密码提示账号不存在或密码错误
销售登记选公司、车牌、粗砂,填 60 元/吨、30 吨总价自动显示 1800.00,日期为当天
销售登记载重留空提交后端拦截或提示必填
销售查询柏松房产 + 1月1日到1月10日 + 未支付只出该范围内未支付订单,汇总金额正确
加班查询选员工 + 起止日期返回区间内总加班时长
其他账目新增油款 75000 未付倒序列表显示在最上面

汇总对账有个习惯我一直在用:页面查完,打开数据库用同一组条件再跑一遍 SELECT SUM,两边数字一致才敢算出账正确。对不上就查分页有没有截断汇总,或者条件参数是不是被前端漏传了。从那以后,我每次做完带统计功能的页面,都会强制走一遍"页面对账"这个动作,拿初始化数据把每个汇总口径验一遍再交出去。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询