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

资讯详情

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

JSP OA系统开发实战:从部署到核心模块与数据库设计全解析

JSP OA系统开发实战:从部署到核心模块与数据库设计全解析 1. 项目概览与需求拆解先聊聊这个项目的来龙去脉。大连东软人才培训中心的OA系统编号tc617功能上就是一个典型的办公自动化系统面向企业内部员工和管理人员解决日常办公中的信息流转问题。这类项目在网上很常见一般以“程序源码数据库调试部署开发环境”的形式打包售卖或作为课程设计使用但我更想说的是拿到一个现成JSP OA项目之后你到底该怎么把它跑起来、改起来、用起来而不是光看它的目录结构就一头雾水。OA系统的核心价值在于“流程”二字。公文审批、会议通知、工作安排、考勤统计、内部公告这些东西如果靠纸质和口头传递效率低且容易漏OA系统做的事情就是把这些流程搬到线上让每一步操作都有记录、可追溯。对于培训中心这类机构来说业务场景还包括学员管理、班级管理、课程安排等所以系统功能会比普通企业的OA多出一些教育业务相关的模块。从技术栈上看这个项目用的是JSP也就是Java Server Pages搭配Servlet、JavaBean、JDBC再加上MySQL数据库和Tomcat服务器。这套组合在现在看起来偏老但在某些特定场景下仍然有它的价值一是学校课程设计、毕业设计还在大量使用二是很多中小型企业内部遗留系统仍然是这个架构三是对于刚入门的Java学习者来说JSP项目是理解HTTP请求、会话管理、数据库操作之间关系的很好媒介。我不建议把它当成现代化开发的范本但作为学习和练手完全没有问题。适合看这篇文章的人主要是两类一类是拿到这个tc617项目不知道怎么跑起来的同学另一类是打算自己从零写一个JSP版OA系统、但摸不清模块边界和数据库设计思路的人。我会把跑通项目的完整路径走一遍同时把核心模块的代码逻辑和数据库设计思路讲透这样你不仅能把这个项目跑起来还能真正看懂它每一步在做什么。2. 系统整体架构与技术选型逻辑2.1 JSP为什么还在被使用在很多技术社区里JSP已经快被划入“古董”行列了但真实情况是仍有大量现网系统跑在JSP上。为什么因为Java的强项从来不是某个炫酷的框架而是生态的稳定性和长期维护能力。很多企业十几年前上了JSP系统数据积累多、业务逻辑复杂迁移成本远大于维护成本所以只好继续用。对应的开发者和运维人员如果懂JSP在这些老系统上反而很好找工作。这个OA项目选择JSP还有一个现实原因课程设计、毕业设计的教学体系还在教。Servlet和JSP是Java Web的基础很多教材和学习路径把Spring Boot放在后面。先把JSP的请求处理、Session管理、JDBC操作搞清楚再学Spring Boot会顺手很多。所以我的观点是JSP不先进但也不丢人它是很好的地基。2.2 标准三层架构在这个项目里的体现这个项目沿用了JSP开发里最常见的JSPServletDAO三层架构JSP层负责页面展示和表单收集也就是用户看到的界面。Servlet层负责接收浏览器请求调用业务逻辑后跳转页面。DAO层Data Access Object负责和数据库打交道执行增删改查。你可以把这三层理解成餐厅的传菜流程。JSP是菜单展示给客户看客户选好菜下单Servlet是传菜员把订单送到后厨再把做好的菜端到客户面前DAO层则是后厨真正负责把食材做成菜也就是数据加工。三层架构最大的价值是解耦。如果需求变化比如把MySQL换成Oracle只需要改DAO层Servlet和JSP基本不用动。如果页面样式调整也只需要改JSP不会牵扯到业务逻辑。这个项目的代码结构清晰基本就是按照这个套路组织的。2.3 Tomcat、MySQL、JDBC的协作方式这个小标题想讲清楚三者的配合关系。浏览器发送请求到Tomcat服务器Tomcat根据web.xml或注解找到对应的Servlet来处理。Servlet里需要往数据库存东西时通过JDBC连接MySQL执行SQL语句拿回结果后再转发或者重定向到某个JSP页面展示给用户。整个链路里Tomcat是Web容器同时管理Servlet的生命周期。MySQL负责持久化存储数据不会因为重启而丢失。JDBC是Java和数据库之间的桥梁它是一套标准接口MySQL驱动只是这套接口的一个具体实现。理解这个协作模型后面调试定位问题就快了。3. 数据库设计详解3.1 核心表结构梳理一个OA系统要干活数据库设计是根基。这个tc617项目的数据库设计比较有代表性覆盖了培训中心的实际业务场景。我挑几个核心表来说。用户表users这张表是整个系统的入口。字段至少包括用户ID、用户名、密码、真实姓名、性别、联系电话、Email、部门ID、角色ID。注意密码在设计初期就不该用明文存储哪怕项目演示版也建议做一次MD5加密避免数据库泄漏时密码直接暴露。部门表department包含部门ID、部门名称、部门描述。用户表和部门表是一对多的关系一个部门下有多个用户。公告表notice包含公告ID、公告标题、公告内容、发布人ID、发布时间、状态。状态字段很有用可以用来表示草稿、已发布、已下线。很多人做OA系统容易忽略这种状态字段后面想要做“只显示未过期公告”就发现还得改表结构。审批表approval这是OA系统的核心业务表。字段包含审批ID、申请标题、申请内容、申请人ID、当前审批人ID、审批状态、创建时间、审批时间、审批意见。审批状态可以用整数表示比如0代表待审批1代表已通过2代表被驳回这样查询和统计都方便。考勤表attendance包含考勤ID、用户ID、签到时间、签退时间、考勤日期、状态。培训中心的场景和普通企业差不多员工上下班打卡、学员上课考勤都可以在这张表上扩展。3.2 表关联关系与SQL设计思路用户表与角色表、部门表通过外键关联公告表的发布人关联用户表审批表的申请人关联用户表考勤表的用户关联用户表。整体来看几乎所有的表都围绕用户表展开这很符合OA系统的特征——它是人的系统用户是核心实体。在设计SQL时要特别注意多表查询的性能。举个常见的场景查询所有待审批的申请单同时需要显示申请人的姓名和部门名称这就要用到JOIN。像这样的SQLSELECT a.approval_id, a.apply_title, a.apply_content, a.apply_time, u.real_name, d.dept_name FROM approval a LEFT JOIN users u ON a.user_id u.user_id LEFT JOIN department d ON u.dept_id d.dept_id WHERE a.approval_status 0这条SQL用了两个LEFT JOIN把申请人姓名和部门信息一次性查出来。如果不用JOIN而是在Java程序里逐条二次查询数据量大了以后性能下降非常明显。初学阶段图省事写N1查询后面数据量上来了就等着被领导找吧。3.3 系统初始化脚本与测试数据项目的数据库文件一般会附带一个.sql脚本这个脚本里除了建表语句还会插入一些测试数据。我看到很多同学拿到的SQL脚本能跑但里面测试数据质量堪忧比如密码明文、中文乱码、时间字段为空。我建议不管你拿到的脚本长什么样导入后先做三件事第一把管理员账号和普通测试账号的密码统一改成自己记得住的值同时看下密码字段是明文还是加密方便后续登录测试。第二检查关键表的自增ID起始值如果已经有测试数据后续插入会不会主键冲突。第三清理掉明显不合理的测试数据比如上千条乱码的公告记录这种数据不处理后面做列表分页测试时会干扰判断。4. 核心功能模块的实现思路4.1 登录鉴权与Session管理OA系统的登录模块是所有模块的地基因为它决定了系统的安全边界。逻辑上用户提交用户名和密码后Servlet接收参数调用DAO层方法查询数据库比对用户名和密码是否匹配。匹配成功后把用户基本信息存到Session里然后重定向到系统主页面。有个细节值得注意重定向用sendRedirect而不是forward。为什么因为重定向会改变浏览器地址栏URL刷新页面时不会再次提交表单避免了用户按F5导致重复登录操作的尴尬。很多新手喜欢用forward写登录逻辑刷新一下页面就说“登录成功又给我跳回去”其实问题出在这儿。Session的操作要规范。登录成功后应该这样写HttpSession session request.getSession(); session.setAttribute(currentUser, user); session.setMaxInactiveInterval(30 * 60);第一个参数设置Session里的用户名或用户对象第二个参数设置Session过期时间30分钟没有操作就失效。退出登录时用session.invalidate()销毁整个Session比单纯移除单个属性更干净。4.2 公告管理与离线判断公告管理模块的逻辑相对简单但有一个点容易被忽略公告的写操作和读操作性能要求完全不同。写操作频率低读操作频率极高。如果系统里同时在线几百人每次打开首页都查一次公告表数据库压力其实不小。简单有效的方案是设置缓存。没有Redis这个重装备时可以用ServletContext应用上下文来缓存公告列表。公告发布或修改时刷新一次缓存其他用户读取时直接返回缓存内容。实现起来代码不多但效果明显。甚至可以用一个静态Map来存加个定时器定期刷新简单粗暴但可行。离线判断指的是每个访问系统的人在打开公告详情页时后台要记录一条查看日志记录谁在什么时间看了哪条公告。这样可以统计公告的触达率对管理人员了解信息是否传达到位很有价值。4.3 审批流的特殊处理这里要重点说审批流。普通的OA审批就是申请-上级审批-结束但培训中心的场景多了一个角色就是学员申请可能还需要教务人员参与审批。这就涉及到了多级审批的规则。我建议在数据库设计阶段就为审批流程表预留一个流程顺序字段比如approval_order。审批流引擎类的东西在这个项目里就不用想了但我们可以做一个简单的状态机状态0待一级审批状态1待二级审批状态2审批通过状态3已驳回。每次审批操作时检查当前状态和操作人角色满足条件才允许更新。前端审批流程的制作对于JSP项目来说通常的做法是JSP页面里用JS和jQuery来做流程图展示。比如用jQuery画出几个审批节点用箭头连起来高亮当前节点。这个纯前端展示不需要后端参与什么复杂逻辑但它能帮用户直观地看到审批流卡在哪里。4.4 前端JSP页面与jQuery交互细节这个项目里前端用的还是传统JSP加jQuery的搭配没有前后端分离。这种模式下JSP文件里既写HTML又写Java代码片段再配合jQuery发AJAX请求。实际开发中最容易踩的坑是JSP代码片段和JavaScript混写时引号转义混乱。比如var userName % session.getAttribute(currentUser) ! null ? session.getAttribute(currentUser).toString() : %;这段代码看起来对但如果你在引号里又加了双引号整个页面就会解析报错。我的习惯是在JSP里先定义好要传给JS的变量再单独引入JS文件避免在JS里写Java标签。页面上如果有表单校验一定要做双重校验。前端用jQuery做第一层校验比如必填项是否为空、邮箱格式是否对后端Servlet再做第二层校验因为攻击者可以绕过前端直接构造HTTP请求前端校验只能提高用户体验不能作为安全边界。4.5 个人信息展示页面的实现每个人登录后都应该有一个自己的信息展示页面显示当前用户的账号、真实姓名、所属部门、角色、最近登录时间等。逻辑上就是根据Session里的用户ID去数据库JOIN查询用户表和部门表把最新的字段值拿出来展示。在JSP页面里要注意个人信息展示页面不应该把用户密码显示出来这是个很基础但经常犯的错误。另外一个好的做法是在页面顶部放一个“编辑资料”入口让用户可以修改联系电话、邮箱这些非关键信息但用户名和角色不能随便改需要管理员权限。5. 开发环境搭建与调试部署5.1 工具链版本选择这个项目用到的开发环境组合比较固定但版本选择上有讲究。JDK推荐JDK 8或JDK 11。JDK 8是最稳的选择Tomcat 8.5和Tomcat 9都能兼容。不要贸然上JDK 17部分老项目在JDK 17上跑会出反射相关的兼容问题。TomcatTomcat 8.5或者Tomcat 9.0。这两个版本对Servlet 3.1/4.0的支持都足够JSP的编译引擎在Tomcat 9里已经换成了Eclipse的JDT兼容性不错。MySQL5.7或者8.0都可以用8.0的驱动类名换成了com.mysql.cj.jdbc.Driver和5.7的驱动类名不一样。如果连不上数据库先检查驱动类名对不对。IDEEclipse IDE for Enterprise Java and Web Developers或者IntelliJ IDEA Ultimate版。IDEA社区版不带Web应用开发插件用到Tomcat集成时要留意。5.2 数据库初始化步骤在MySQL里初始化这个项目的数据库一般用命令行或者Navicat执行SQL脚本。命令行导入方式如下mysql -u root -p CREATE DATABASE oa_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE oa_system; SOURCE D:/path/to/oa_system.sql;数据库的字符集记得要指定utf8mb4。很多JSP项目乱码的根源就是数据库默认字符集和项目编码不一致最后页面上显示一堆问号。如果检查发现数据库连接URL里面没有characterEncodingutf8参数也要补上例如jdbc:mysql://localhost:3306/oa_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseserverTimezone这个参数在MySQL 8.0里必填否则报时区错误。useSSLfalse是为了避免本地调试时证书校验的干扰生产环境另说。5.3 Tomcat部署配置拿到源码后通常是一个Eclipse或IDEA的Web项目结构。在Eclipse里导入后需要在Project Facets里确认Dynamic Web Module版本是否正确Build Path里是否有Tomcat的运行时库。部署到Tomcat有两种常见方式。第一种是开发模式下直接在IDE里右键“Run on Server”IDE会自动把项目发布到Tomcat的webapps目录。第二种是手动部署把项目打成一个WAR包放到Tomcat的webapps目录下启动Tomcat自动解压。手动打WAR包的命令jar -cvf oa_system.war -C D:/workspace/oa_system/WebRoot .把生成的war文件放入Tomcat的webapps目录然后启动Tomcatcd D:/apache-tomcat-9.0.XX/bin startup.bat启动后浏览器访问http://localhost:8080/oa_system/如果项目直接部署在ROOT目录访问路径就是http://localhost:8080/。很多同学第一次部署后访问404十有八九是项目不是ROOT应用、忘了加上项目名路径。5.4 常见部署报错与修复把我在调试这类项目时最常见的报错整理成一张表方便直接对照排查。报错场景根本原因解决方案页面中文乱码数据库字符集、连接URL、JSP页面编码不一致统一使用UTF-8检查三个位置数据库连接失败Communications link failureMySQL服务未启动或驱动版本不匹配启动MySQL服务确认mysql-connector-java版本java.lang.ClassNotFoundException: com.mysql.jdbc.Driver驱动JAR包没有放到WEB-INF/lib目录把mysql-connector-java JAR包拷贝到lib目录HTTP Status 404访问路径或项目名不对确认部署上下文路径检查web.xml中Servlet映射端口被占用8080被其他程序占用修改Tomcat的server.xml端口或关闭占用的程序Session失效后页面报空指针取Session属性时没有判空在Servlet中先判断用户是否已登录数据库表不存在环境配置了错误数据库名检查JDBC URL里的库名和配置的数据库名是否一致5.5 前端资源加载问题JSP项目的页面里会有大量CSS、JS、图片资源的引用。部署后如果发现页面样式混乱或JS不执行先打开浏览器F12看Console和Network。大概率是资源的路径写死导致的例如在页面里直接写了C盘绝对路径或者在相对路径的计算上出了问题。建议在JSP页面头部定义基础路径% String basePath request.getScheme() :// request.getServerName() : request.getServerPort() request.getContextPath() /; %然后在引用静态资源时统一使用%basePath%拼前缀。这样无论项目部署在哪个上下文路径下资源都能正确加载。6. 实际调试中遇到的典型问题与排查实录6.1 登录后页面跳转出现500错误我调试这个tc617项目时第一次登录后进入主页面就报了500错误。排查步骤是先看Tomcat的catalina.out日志报错定位到一个JSP页面里的空指针。原因是系统管理员的角色ID为1但是在角色表里没有对应的角色记录导致通过JOIN查出来的角色名称为null页面在显示角色名时直接调用了.toString()方法就炸了。解决办法很简单页面里加上空值判断或者SQL里改用LEFT JOIN并加IFNULL函数处理。这个案例说明一个问题就是拿到的测试数据往往不完整配套的角色表、部门表数据缺失很常见跑通项目之前先检查辅助表的数据完整性。6.2 SQL语句在MySQL中能跑、在Java程序里报错这个问题的根源往往不是SQL本身而是编译和运行时环境差异。常见的坑是SQL里用了MySQL 8.0才支持的特性和函数而驱动版本是老的5.x导致功能不兼容。还有一种情况是SQL里写了中文注释或中文条件而Java文件保存的编码不是UTF-8编译后中文变乱码SQL自然匹配不到。排查这个问题的顺序是先在Navicat或命令行中执行同样SQL确认语法没问题然后在代码里把SQL打印出来用System.out.println输出到控制台看实际执行的SQL和预期是否一致最后检查Java文件编码和数据库连接URL最快的方法是在IDEA的右下角把文件编码改成UTF-8重新编译。6.3 数据库连接池耗尽这个项目在多人同时使用的时候可能会出现数据库连接超时或连接池耗尽的报错。原因一般是代码里没有及时关闭JDBC连接或者使用了错误的获取连接方式。很多JSP项目习惯在DAO层里直接写Connection conn DBUtil.getConnection(); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql);如果忘记在finally块关闭连接每次操作都会占一个连接连接池很快就被耗光。正确的写法是在finally里关闭或者用try-with-resources语法try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { // 处理结果 } catch (SQLException e) { e.printStackTrace(); }这个写法会自动关闭资源代码也简洁不少。这个项目里我已经把核心DAO层全部改成这种写法了测试下来连接不复用时也没有再报警。6.4 文件导出功能在本地正常、部署到服务器失效如果系统里有导出Excel或Word的功能本地正常、部署后失效的问题也常见。多半是服务器的文件目录权限和本地不一样或者导出时写死的临时路径在服务器上不存在。建议把所有文件操作都改为相对路径基于Tomcat的临时目录System.getProperty(java.io.tmpdir)来存储不要写死D盘或C盘。7. JSP项目的下一步演进看过不少JSP老项目的代码之后我的想法是别急着嘲笑技术旧也别一股脑全部重写。这个tc617项目本身的结构其实还可以三层架构、模块划分、数据库设计都有模有样最大的问题是技术栈陈旧和部分代码不够规范。如果你毕业设计做的是这个方向或者工作中接到了一个类似的JSP遗留系统我的建议是分三步走。第一步是性能微调。把JDBC连接换成连接池比如HikariCP把JSP里嵌入的Java代码尽量扣出来放到Servlet后面对核心表建好索引减少慢查询。这些工作不需要改整体架构收益却很直接。第二步是局部替换。前端引入一些现代但不过于复杂的交互方式比如用Vue.js或原生JS替换掉老旧的jQuery插件登录页面和服务端交互改成AJAX方式。JSP页面可以继续保留但页面内部的数据渲染部分可以逐步推到前端让JSP专注于后端模板能力。第三步才是全面重写。如果业务确实复杂到老架构扛不住了或者人员流动导致维护成本极高那才考虑迁移到Spring Boot MyBatis Plus Vue这套现代化组合。迁移时不要想着一次性切换按模块灰度替换才是稳妥的。拿到一份JSP OA系统项目的源码后请记住能跑起来只是第一步。真正有价值的是看懂它的数据库设计思路、模块拆分逻辑、权限控制方式和审批流处理方式然后把这份理解迁移到新项目里这才是最有用的经验积累。
返回列表