Java Web环境监测系统:JDBC+Servlet实战闭环
发布时间:2026/9/11 23:34:20来源:尧图网络
简介本资源是一套基于Java开发的环境监测系统毕业设计源码面向计算机专业本科生及初级Java开发者聚焦环境数据采集、分析与可视化场景解决课程设计、毕设选题与工程实践中的典型需求。压缩包共46个文件含35个Java核心类文件覆盖DAO层、Service层、Controller层及DTO数据模型辅以数据库设计文档、更新脚本与README说明结构完整体现MVC分层架构包体仅29KB轻量易读适合作为SpringServletMySQL技术栈的入门级全栈学习范例。已有701人学习下载读者可直接获取可运行的模块化代码结构、清晰的监控管理与用户登录功能实现逻辑以及ph值、温度、CO₂等多维度环境数据的分层处理方案快速掌握环境监测类系统的开发流程与关键设计模式。1. 这不是又一个“登录列表”的Java毕业设计而是一套可跑通的环境监测业务闭环你下载了school-master.zip解压后看到满屏的PhdataInfoDaoImpl.java、Co2InfoController.java、TemperatureInfoService.java……第一反应可能是“又一个 CRUD 堆出来的毕设”但真正打开PhInfoController.java和Setco2dataInfoController.java你会发现它在处理 pH 值校准阈值告警、CO₂ 数据按分钟级聚合入库、温湿度双因子联动判断设备运行状态——这些不是模板代码而是真实环境监测场景中必须落地的逻辑。这个系统不依赖前端框架渲染却通过标准 Servlet JSP 实现了数据采集端模拟传感器、管理端多角色权限、展示端折线图阈值标红三端协同它没用 Spring Boot 自动装配但LoginServiceImpl里手写的密码加盐逻辑、AdminloginInfoDaoImpl中基于PreparedStatement的防 SQL 注入写法恰恰是 Java Web 工程师在中小项目中真正要写的代码。适合正在做毕业设计、需要快速验证 MVC 分层合理性、或想补足 JDBC Servlet 原生开发手感的开发者——尤其当你被要求“不能直接抄 Spring Boot 脚手架”时这套代码就是能过答辩、能现场演示、能改出自己业务逻辑的底盘。2. 从数据库 ER 图到 DAO 层实现为什么用 MySQL JDBC 而不是 MyBatis2.1 环境监测核心实体建模与字段语义解析系统围绕四大监测维度构建数据表phinfopH 值、temperatureinfo温度、co2info二氧化碳浓度、co2datainfoCO₂ 历史采样点。每张表均含id主键、value实测数值、unit单位如 ℃、ppm、record_time精确到秒的时间戳、device_id设备编号字段。特别注意phinfo表中is_abnormal是否异常和abnormal_reason异常原因字段——这不是装饰性字段而是PhInfoService.java中checkPhValue()方法的输出结果当value 6.5 || value 8.5时置为true并记录超出水体安全范围。这种将业务规则下沉至数据库字段的设计避免了前端反复调用校验接口也使历史数据分析可直接WHERE is_abnormal 1聚合统计。adminlogininfo和userlogininfo表分离管理员与普通用户role_type字段值为admin或operator而非布尔型为未来扩展多角色如auditor预留空间。2.2 手写 JDBC DAO 层的关键实现细节系统未使用 MyBatis 或 Hibernate所有 DAO 均继承自BaseDao虽未在文件列表中显式列出但PhdataInfoDaoImpl.java中extends BaseDaoPhdataInfo可反推其存在核心是executeUpdate和executeQuery的封装。以Co2InfoDaoImpl.java为例public class Co2InfoDaoImpl extends BaseDaoCo2Info implements Co2InfoDao { Override public void insert(Co2Info co2Info) throws SQLException { String sql INSERT INTO co2info (value, unit, record_time, device_id) VALUES (?, ?, ?, ?); Object[] params {co2Info.getValue(), co2Info.getUnit(), co2Info.getRecordTime(), co2Info.getDeviceId()}; executeUpdate(sql, params); } Override public ListCo2Info findByTimeRange(String startTime, String endTime) throws SQLException { String sql SELECT * FROM co2info WHERE record_time BETWEEN ? AND ? ORDER BY record_time DESC; Object[] params {startTime, endTime}; return executeQuery(sql, params, Co2Info.class); } }提示executeQuery方法内部使用ResultSetMetaData动态映射字段到Co2Info对象属性要求数据库列名与 Java Bean 属性名严格一致如record_time→recordTime否则需在BaseDao中重写mapToEntity方法。这是手写 DAO 的典型约束也是理解 ORM 原理的必经之路。2.3 连接池配置与事务边界控制src/main/resources/db.properties文件需自行创建应包含drivercom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/env_monitor?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue usernameroot password123456 initialSize5 maxActive20 maxWait60000BaseDao.java中getConnection()方法从DruidDataSource获取连接executeUpdate方法内显式调用conn.setAutoCommit(false)并在try-catch-finally块中控制conn.commit()或conn.rollback()。例如Co2dataInfoServiceImpl.java的batchInsert方法在插入 100 条 CO₂ 数据时若第 50 条失败则前 49 条全部回滚——这保证了监测数据的时间序列完整性避免出现“某分钟数据缺失半条”的脏状态。2.4 与常见误用的对比为什么不用 Hibernate 的GeneratedValue部分学生毕设盲目使用Id GeneratedValue(strategy GenerationType.IDENTITY)导致 MySQL 主键自增与 Java 对象 ID 不同步。本系统所有insert方法均不设置id字段依赖数据库AUTO_INCREMENTexecuteUpdate返回int类型的generatedKeys再通过rs.getLong(1)获取新 ID 并赋值给对象。这种显式处理虽繁琐但规避了 Hibernate 在批量插入时因缓存策略导致的主键错乱问题——在环境监测场景中每条数据的id是后续告警溯源的关键索引不容出错。3. Controller 层路由设计与业务逻辑分层从登录鉴权到实时数据推送3.1 多 Controller 协同处理监测流系统采用传统 Servlet 模式web.xml中配置如下映射servlet servlet-nameLoginController/servlet-name servlet-classcontroller.LoginController/servlet-class /servlet servlet-mapping servlet-nameLoginController/servlet-name url-pattern/login/url-pattern /servlet-mapping !-- 其他 Controller 类似 --关键在于Co2InfoController.java与Setco2dataInfoController.java的分工前者响应/co2/list请求返回近 24 小时 CO₂ 数据 JSON后者响应/co2/set请求接收{deviceId:DEV001,threshold:1000}格式参数调用Co2InfoService.setThreshold()更新设备告警阈值。这种分离使“数据查询”与“策略配置”解耦符合 RESTful 设计原则GET /co2/list vs POST /co2/set也为后续接入 MQTT 设备端指令提供扩展点。3.2 登录鉴权的三层校验链LoginController.java的doPost方法执行三步校验空值校验检查request.getParameter(username)是否为空返回response.getWriter().print({\code\:400,\msg\:\用户名不能为空\});密码比对调用LoginService.login(username, password)该方法在LoginServiceImpl.java中先查adminlogininfo表若无匹配则查userlogininfo表不合并查询避免UNION导致的 SQL 注入风险会话绑定校验成功后request.getSession().setAttribute(user, user)其中user对象含roleType字段后续所有 Controller 通过session.getAttribute(user)获取并判断权限注意LoginService.java中密码比对使用BCryptPasswordEncoder需引入spring-security-crypto依赖而非明文equals()。encodePassword()方法在用户注册时调用matches()方法在登录时验证这是 Java Web 安全的基线要求。3.3 实时数据推送的轻量级实现系统未用 WebSocket而是采用Servlet 3.0的AsyncContext实现长轮询。TemperatureInfoController.java中WebServlet(urlPatterns /temperature/realtime, asyncSupported true) public class TemperatureInfoController extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final AsyncContext asyncContext req.startAsync(); asyncContext.setTimeout(30000); // 30秒超时 new Thread(() - { try { // 每5秒查一次最新温度 while (!asyncContext.hasExpired()) { ListTemperatureInfo latest temperatureInfoService.getLatest(1); if (!latest.isEmpty()) { String json new ObjectMapper().writeValueAsString(latest); resp.setContentType(application/json;charsetUTF-8); resp.getWriter().write(json); break; // 推送后立即结束 } Thread.sleep(5000); } } catch (Exception e) { e.printStackTrace(); } finally { asyncContext.complete(); } }).start(); } }此方案兼容老版本 Tomcat且避免 WebSocket 连接管理的复杂度。前端 JavaScript 只需fetch(/temperature/realtime)收到数据后更新 DOM无需维护连接状态。3.4 DTO 与 VO 的明确边界co2Info包下Co2Info.java是数据库实体含id,record_time而controller包中Co2InfoController.java返回的 JSON 数据由Co2InfoVO.java封装public class Co2InfoVO { private String timeStr; // 格式化为 HH:mm:ss private Double value; private String status; // 正常 / 超标根据阈值计算 // getter/setter }Co2InfoService.java中convertToVO(ListCo2Info)方法完成转换确保 Controller 不暴露数据库字段细节也便于前端直接消费。这种分层杜绝了“把 Entity 直接ResponseBody返回”的反模式。4. 前端 JSP 页面与数据可视化用原生 JS 驱动 ECharts 折线图4.1 JSP 页面结构与动态数据注入WEB-INF/jsp/monitor.jsp是核心监控页关键片段% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title环境监测实时看板/title script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script /head body div idco2Chart stylewidth: 600px;height:400px;/div script // 服务端注入初始数据 const initCo2Data c:out value${co2DataJson} escapeXmlfalse/; const chart echarts.init(document.getElementById(co2Chart)); chart.setOption({ title: { text: CO₂浓度实时曲线 }, tooltip: { trigger: axis }, xAxis: { type: category, data: initCo2Data.times }, yAxis: { type: value }, series: [{ name: CO₂(ppm), type: line, data: initCo2Data.values, markLine: { data: [{ type: average, name: 平均值 }] } }] }); /script /body /htmlco2DataJson由Co2InfoController.java的request.setAttribute(co2DataJson, jsonStr)注入jsonStr格式为{times:[09:00,09:01,...],values:[850,852,...]}。这种服务端预渲染方式避免了前端 AJAX 请求的跨域和 loading 状态管理问题。4.2 阈值告警的视觉强化实现ECharts 配置中增加markArea突出危险区间markArea: { itemStyle: { color: rgba(255, 0, 0, 0.1) }, data: [[ { xAxis: 09:00 }, { xAxis: 17:00, yAxis: 1000 } // 超标阈值线 ]] }同时Co2InfoService.java的getLatestWithStatus(int count)方法返回ListCo2InfoVO其中status字段根据value threshold计算JSP 中c:if test${vo.status 超标}渲染红色背景的表格行——数据层驱动 UI 状态而非前端硬编码阈值。4.3 多图表联动的事件监听monitor.jsp中温度、pH、CO₂ 三个图表共享同一时间轴。通过chart.on(dataZoom, function(params) {...})监听缩放事件触发其他图表同步更新// 温度图表监听 tempChart.on(dataZoom, function(params) { // 向 CO₂ 图表发送相同 zoom 参数 co2Chart.dispatchAction({ type: dataZoom, start: params.start, end: params.end }); phChart.dispatchAction({ type: dataZoom, start: params.start, end: params.end }); });此方案无需后端 API 支持纯前端实现降低服务器压力符合轻量级监测系统定位。5. 部署调试与性能优化Tomcat 本地部署及慢查询定位5.1 Tomcat 8.5 部署关键配置将school-master项目导入 Eclipse/IDEA 后需确认Java 版本pom.xml或项目属性中设为1.8javac -version输出1.8.0_3XX因Druid连接池 1.2.x 不支持 JDK 17Web 应用路径Properties → Web Project Settings → Context root设为/env-monitor避免localhost:8080/school-master/login的冗余路径数据库驱动WEB-INF/lib/下必须有mysql-connector-java-8.0.33.jar且db.properties中driver为com.mysql.cj.jdbc.Driver启动后访问http://localhost:8080/env-monitor/login.jsp若报ClassNotFoundException: com.mysql.cj.jdbc.Driver检查 JAR 包是否在WEB-INF/lib且未被 Maven 依赖冲突覆盖。5.2 慢查询日志开启与分析在 MySQL 配置文件my.cnf中添加[mysqld] slow_query_log ON slow_query_log_file /var/log/mysql/mysql-slow.log long_query_time 1 log_queries_not_using_indexes ON重启 MySQL 后执行SELECT * FROM co2info WHERE record_time 2024-01-01无索引日志中将记录# Time: 2024-06-15T10:23:45.123456Z # UserHost: root[root] localhost [127.0.0.1] Id: 123 # Query_time: 2.345678 Lock_time: 0.000123 Rows_sent: 10000 Rows_examined: 50000 use env_monitor; SELECT * FROM co2info WHERE record_time 2024-01-01;此时需为record_time字段添加索引ALTER TABLE co2info ADD INDEX idx_record_time (record_time);同理phinfo、temperatureinfo表均需对record_time建索引。这是环境监测系统高频查询的刚需优化否则单表 10 万行数据查询将超 2 秒。5.3 内存泄漏排查ServletContext 监听器实践src/main/java/listener/AppContextListener.java需自行创建用于监控应用生命周期public class AppContextListener implements ServletContextListener { Override public void contextInitialized(ServletContextEvent sce) { System.out.println(【EnvMonitor】应用启动初始化数据源...); // 初始化 DruidDataSource DruidDataSource dataSource new DruidDataSource(); // ... 配置 dataSource sce.getServletContext().setAttribute(dataSource, dataSource); } Override public void contextDestroyed(ServletContextEvent sce) { System.out.println(【EnvMonitor】应用关闭释放数据源...); DruidDataSource dataSource (DruidDataSource) sce.getServletContext().getAttribute(dataSource); if (dataSource ! null) { dataSource.close(); // 关键防止连接池线程残留 } } }web.xml中注册listener listener-classlistener.AppContextListener/listener-class /listener此监听器确保 Tomcat 停止时优雅关闭连接池避免java.lang.OutOfMemoryError: unable to create new native thread错误——这是 Java Web 项目部署后最常被忽视的内存泄漏点。5.4 生产环境参数调优表参数默认值推荐值作用maxActive(Druid)820提升并发查询能力适配多传感器数据写入validationQuery无SELECT 1开启连接有效性检测避免 stale connectionremoveAbandonedOnBorrowfalsetrue自动回收超时未归还连接防连接泄露JVM Heap(Tomcat)-Xms512m -Xmx512m-Xms1024m -Xmx2048m环境监测数据可视化需更多堆内存调整后jstat -gc pid观察S0C,S1C,EC,OC使用率确保 Full GC 频率低于 1 次/小时。本文还有配套的精品资源点击获取
网站建设高端定制企业官网