简介:基于Java的固定资产管理系统毕业设计文档,是一份完整的论文资料,面向计算机专业学生、毕业设计者及希望掌握SSH框架的开发者。文档以某公司固定资产管理为背景,采用浏览器/服务器模式,运用JSP、Struts、Hibernate、Spring技术栈,详细阐述了系统需求分析、架构设计、功能实现与权限管理,涵盖资产增删改查、报表打印、职工信息管理及员工权限分配等核心模块,并给出了清晰的数据库与界面设计思路。包体仅含1个doc文档,大小2.72MB,内容充实,目录结构完整,从摘要、Abstract、绪论到系统分析、详细设计等章节一应俱全,便于直接参考论文框架或提炼技术要点。目前已有45人学习,对于准备毕业设计或课程项目的读者具有较高参考价值,可帮助理解SSH整合原理、资产管理业务流程以及论文撰写规范,也能为实际信息系统开发提供切实可行的设计思路。
1. 为什么毕业设计都爱选SSH做固定资产管理
做管理系统类毕业设计时,固定资产管理是一个被反复验证过的好题目:业务边界清晰、角色分明、增删改查覆盖完整,还能带出报表和权限这类“加分项”。这个基于Java的公司固定资产管理系统,就是典型的SSH组合——JSP负责页面展示,Struts接住请求,Spring管对象,Hibernate做持久化。整套代码对初学者最友好的地方在于,它把MVC的每个环节都摆在了明面上:你在Action里写业务逻辑,在Service里调DAO,在Hibernate映射文件里定义表和对象的关系,每一步都能找到对应位置。
这套系统对应的是龙成公司的实际管理场景,资产从入库到借出、归还、维护都有记录,管理员的权限高于操作员。对于准备java毕业设计的人,它既是能直接跑的完整项目,也是一份看得懂的分层教材。适合两类人:第一是计算机专业学生,拿它理解SSH整合流程;第二是刚工作的Java工程师,想复盘传统B/S系统里数据流和控制流的组织方式。下面直接从架构选型说起。
2. 固定资产系统的SSH架构分层与B/S选型逻辑
2.1 为什么选JSP+Struts而不是Servlet
这个项目是2019年的毕业设计,但它背后的技术选型逻辑到现在仍然适用。先用一张图理解请求流转路径:浏览器发出HTTP请求,Tomcat根据web.xml把请求交给Struts的ActionServlet,ActionServlet读取struts-config.xml找到对应Action,Action调用Spring管理的Service,Service调用Hibernate的DAO接口,最终通过Hibernate模板操作MySQL数据库。数据返回时逆向走一遍,JSP负责渲染。
相比直接写Servlet,Struts的核心价值在于把请求分发和业务逻辑解耦。比如资产查询和资产删除是两个不同的Action,但它们的公共逻辑,比如字符编码过滤、权限校验,可以在Struts的拦截器或过滤器里集中处理。实际开发中,我一般会建议在Action里只做参数收集和视图跳转,真正的业务判断放到Service层,这样便于用Spring的AOP做事务控制。
代码层面的跳转配置如下:
<action path="/asset/add" type="com.company.sshaction.AssetAction" name="assetForm" scope="request" validate="true" parameter="method"> <forward name="success" path="/asset/list.jsp"/> <forward name="error" path="/asset/add.jsp"/> </action>参数说明:path是浏览器访问的URL路径;type是对应的Action全类名;name引用表单Bean,Struts会自动把请求参数封装进AssetForm对象;parameter="method"是常用的DispatchAction技巧,同一个Action类可以通过?method=add或?method=delete调用不同方法,减少Action类的数量。
2.2 Spring在系统中的角色:不只是粘合剂
很多人初学SSH时会把Spring当成一个“注册中心”,其实它在固定资产系统里承担了三件事:依赖注入、事务管理和AOP权限校验。比如资产借出时需要同时更新资产状态表和新增借还记录,这两步要么都成功要么都回滚,Spring声明式事务就能保证:
<bean id="assetService" class="com.company.service.AssetService"> <property name="assetDao" ref="assetDao"/> <property name="borrowDao" ref="borrowDao"/> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <aop:config> <aop:pointcut id="serviceMethods" expression="execution(* com.company.service.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/> </aop:config>这里有个关键点:AssetService里同时注入了assetDao和borrowDao,事务切面拦截所有com.company.service包下的方法。当一个Service方法内部多次调用DAO时,它们共享同一个数据库连接,如果中间抛出运行时异常,事务自动回滚,不需要手写try-catch。
2.3 Hibernate映射文件的设计细节
资产表和员工表是Hibernate的核心映射对象。早期SSH项目习惯用.hbm.xml文件而不是注解,因为这种方式可以更精细地控制字段类型和关联关系。看资产表的一个映射片段:
<class name="com.company.entity.Asset" table="t_asset"> <id name="id" type="java.lang.Integer"> <column name="id"/> <generator class="native"/> </id> <property name="assetNo" type="java.lang.String"> <column name="asset_no" length="50" not-null="true"/> </property> <property name="assetName" type="java.lang.String"> <column name="asset_name" length="100" not-null="true"/> </property> <property name="status" type="java.lang.Integer"> <column name="status"/> </property> <!-- status字段含义:0=在库,1=已借出,2=维修中,3=报废 --> <many-to-one name="employee" class="com.company.entity.Employee" fetch="select"> <column name="employee_id"/> </many-to-one> </class>映射文件里最容易被忽略的是many-to-one关系。资产表通过employee_id外键关联员工表,表示“当前保管人”。实际查询时,如果直接访问asset.getEmployee().getName()会触发Hibernate的懒加载,在Session关闭后报LazyInitializationException。解决办法是在Service层内把需要展示的数据查询完成,或者将关联对象设为fetch="join",这要根据业务场景权衡。
3. 数据库设计与核心功能模块实现
3.1 数据表结构设计要点
固定资产管理的数据模型并不复杂,但表之间的关系值得拆解。从论文中的功能描述看,系统至少需要五张核心表:用户表、员工表、资产表、资产借还表、资产维护表。下面给出资产和借还表的基本设计:
| 字段名 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | int | 主键,自增 | 资产ID |
| asset_no | varchar(50) | 非空,唯一 | 资产编号 |
| asset_name | varchar(100) | 非空 | 资产名称 |
| category | varchar(50) | 可空 | 资产分类,如电子设备、家具 |
| price | decimal(10,2) | 非空 | 资产原值 |
| status | int | 默认0 | 0在库 1借出 2维修 3报废 |
| employee_id | int | 外键 | 当前保管人ID |
| buy_date | datetime | 可空 | 购入日期 |
| remark | varchar(255) | 可空 | 备注 |
注意status字段设计了枚举值,查询时不需要关联表就能知道资产状态。asset_no加唯一约束可以防止重复录入。员工表和用户表是分离的,用户表存登录账号密码和角色,员工表存姓名、部门等基本信息,这种设计保证了操作员账号被删除时员工历史记录仍保留。
3.2 资产录入的Action实现流程
资产入库是整个系统的操作入口。前端JSP表单通过AssetForm收集参数,Struts负责参数校验,校验通过后交给Service。看一个简化的Action方法:
public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { AssetForm assetForm = (AssetForm) form; Asset asset = new Asset(); asset.setAssetNo(assetForm.getAssetNo()); asset.setAssetName(assetForm.getAssetName()); asset.setCategory(assetForm.getCategory()); asset.setStatus(0); // 新入库资产默认在库 // 用SimpleDateFormat处理日期字符串,避免中文环境下的格式问题 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { asset.setBuyDate(sdf.parse(assetForm.getBuyDate())); } catch (ParseException e) { e.printStackTrace(); return mapping.findForward("error"); } // 保存人从Session中获取,保证操作可追溯 User operator = (User) request.getSession().getAttribute("currentUser"); asset.setCreateUser(operator.getUsername()); assetService.addAsset(asset); return mapping.findForward("success"); }这段代码里有两个工程习惯值得学习:一是保存操作人信息到数据表而不是只靠日志,后续审计时能直接从数据库查出谁在什么时间录入了哪些资产;二是日期解析单独处理,Hibernate的日期类型与MySQL的datetime字段对应时容易产生时区偏移,统一使用SimpleDateFormat并明确格式可以减少这类问题。实际项目中,AssetForm里还应该加ActionErrors做必填项校验,比如资产编号为空时返回错误信息而不进入Service层。
3.3 管理员与操作员的权限控制
论文中提到“操作员只可对资产和用户进行基本操作,管理员可以为每位员工赋权限”,这就是典型的基于角色的访问控制。Struts中实现权限控制有两种常见做法:在Action执行前用Servlet过滤器拦截,或者在Spring的AOP里做方法级拦截。
更推荐的是在Struts的RequestProcessor里做统一校验,这样不需要在每个Action中重复写权限判断逻辑:
public class SecurityProcessor extends RequestProcessor { @Override protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) { String path = request.getServletPath(); // 登录和公共页面直接放行 if ("/login.jsp".equals(path) || "/login.do".equals(path)) { return true; } HttpSession session = request.getSession(); User user = (User) session.getAttribute("currentUser"); if (user == null) { try { response.sendRedirect(request.getContextPath() + "/login.jsp"); } catch (IOException e) { e.printStackTrace(); } return false; } // 管理员角色可以访问以manage开头的路径 if ("/manage_".contains(path) && !"admin".equals(user.getRole())) { try { response.sendRedirect(request.getContextPath() + "/error.jsp"); } catch (IOException e) { e.printStackTrace(); } return false; } return true; } }注意这个类实现的processPreprocess是Struts1.2之后提供的方法,相比直接继承ActionServlet,它更轻量。配置时需要在struts-config.xml里替换controller属性:
<controller processorClass="com.company.security.SecurityProcessor"/>这样做的好处是权限逻辑集中且可测试。如果项目里再引入Spring Security,思路也是类似的:定义资源与角色的映射关系,在请求进入控制器之前完成认证和授权。
4. 资产借还、报表打印与中文乱码实战
4.1 借出与归还的状态管理
资产借还模块最容易出现的数据问题是不一致:资产记录显示在库,但借还表里没有对应记录。为了避免这种情况,借出操作必须在一个事务里完成两件事:插入一条借还记录,同时更新资产表的status和employee_id。对应的Service方法:
public void borrowAsset(Integer assetId, Integer employeeId, Date borrowDate) { Asset asset = assetDao.findById(assetId); // 校验资产当前状态,已借出或维修中的资产不能再借 if (asset == null || asset.getStatus() != 0) { throw new BusinessException("资产不存在或当前状态不可借出"); } BorrowRecord record = new BorrowRecord(); record.setAsset(asset); record.setEmployee(employeeDao.findById(employeeId)); record.setBorrowDate(borrowDate); borrowDao.save(record); asset.setStatus(1); asset.setEmployee(employeeDao.findById(employeeId)); assetDao.update(asset); }这里有一个容易被忽略的坑:asset是Hibernate的持久化对象,在事务内修改它的属性后,即使不显式调用update,事务提交时也会自动更新数据库。但为了明确表达意图,代码里还是保留了assetDao.update(asset)。借出时校验状态是必须的,如果不校验,同一台设备可能被借给两个人。
HQL查询借出记录时,我一般使用以下写法:
String hql = "from BorrowRecord r where r.returnDate is null"; Query query = session.createQuery(hql); List<BorrowRecord> list = query.list();这个HQL只查询尚未归还的借出记录。returnDate为空表示还在借出状态,这是最直观的状态标记。实际运行中要注意HQL中的类名和属性名大小写,BorrowRecord对应Java类名,returnDate对应属性名而不是数据库的return_date列名,写错会直接抛QuerySyntaxException。
4.2 使用JavaReport生成打印报表
论文中提到的外部包JavaReport-V3-Enterprise-Released.jar是当时的报表组件。打印报表的流程可以拆成四个步骤:从数据库查出要打印的数据集合,把数据填充到报表模板,设置输出格式,最后通过Response输出流写回浏览器。
public void printAssetList(HttpServletRequest request, HttpServletResponse response) { List<Asset> assets = assetService.findAllAssets(); request.setAttribute("assets", assets); // 报表组件通过JSON或XML接收数据,这里用字符串拼接简化 StringBuilder json = new StringBuilder(); json.append("["); for (Asset a : assets) { json.append(String.format( "{\"assetNo\":\"%s\",\"assetName\":\"%s\",\"price\":\"%.2f\",\"status\":\"%s\"},", a.getAssetNo(), a.getAssetName(), a.getPrice(), a.getStatus())); } if (assets.size() > 0) { json.deleteCharAt(json.length() - 1); } json.append("]"); // 转换JSON为报表组件需要的DTO response.setContentType("application/pdf"); response.setCharacterEncoding("UTF-8"); // 调用JavaReport的ReportManager输出PDF // ReportManager.createReport(json.toString()).exportPDF(response.getOutputStream()); }实际项目中不建议手写JSON,可以用fastjson或Gson替代。报表打印的注意点在于中文显示,PDF报表引擎默认字体可能不支持中文,需要在报表模板或代码里指定中文字体,比如STSong-Light,否则导出的PDF中文会是乱码或方块。
4.3 中文乱码的三层过滤方案
中文乱码是所有老Java Web项目绕不开的话题,这个系统也不例外。乱码会产生在三个位置:页面显示乱码、请求参数乱码、数据库存储乱码。工程上常用的做法是统一编码为UTF-8。
第一层,在JSP页面顶部声明:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>第二层,使用过滤器统一处理POST请求编码:
public class EncodingFilter implements Filter { private String encoding = "UTF-8"; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(encoding); response.setCharacterEncoding(encoding); chain.doFilter(request, response); } }第三层,修改MySQL连接URL和环境变量,这是最容易被忽略的环节:
jdbc:mysql://localhost:3306/assetdb?useUnicode=true&characterEncoding=UTF-8useUnicode=true和characterEncoding=UTF-8必须同时出现,否则Hibernate连接MySQL时中文写入会变??。另外,如果使用的是MySQL 5.7及以上版本,还需要在my.ini中设置character-set-server=utf8mb4,因为utf8mb4才是完整的四字节UTF-8支持。
5. 从论文到可演示系统:复现与答辩的实操技巧
5.1 将MyEclipse项目迁移到IDEA
论文指定的开发环境是MyEclipse 8.0和Tomcat 6.0,如果你现在打开这个项目,很可能会遇到编译不兼容的问题。我的建议是迁移到IntelliJ IDEA和Tomcat 8.5以上版本。迁移时重点检查三处:项目结构是否为src/main/java布局;.classpath中是否残留MyEclipse的容器引用;web.xml中servlet版本是否与Tomcat匹配。Struts1和Spring3在Tomcat 8上运行没有问题,但要注意删除xercesImpl.jar这类可能引起冲突的XML解析包。
5.2 功能测试的关键数据准备
测试这个系统时不要只登录管理员账号随便点两下。建议按照真实流程准备三组数据:第一组是正常数据,比如“笔记本-001”从入库到借出再到归还;第二组是边界数据,比如资产编号为空、价格超过百万、员工姓名为生僻字;第三组是异常操作,比如重复删除已报废资产、操作员尝试访问管理员页面。验收时重点观察三件事:操作是否产生预期记录、借出后资产状态是否变化、错误提示是否清晰定位到具体字段。
5.3 答辩时把三个技术点讲透
面试官或答辩老师通常不会只问“你做的是什么”,而是追问“为什么这么设计”。这个固定资产系统有三个技术点可以提前准备。
第一个是Hibernate的一级缓存和懒加载。回答时要说到Session范围内的缓存机制,以及打开懒加载后Session关闭导致LazyInitializationException的解决方案。
第二个是Spring的事务管理粒度问题。你可以在答辩时说:不是所有方法都需要事务,查询方法设置只读事务readOnly=true可以提升性能,而borrowAsset这样涉及两次写操作的方法必须开启默认的REQUIRED传播行为。
第三个是权限控制的边界。只在前端隐藏“管理员管理”按钮是不够的,因为请求还是可以直接发到后台。你的SecurityProcessor在服务端做了二次校验,这才是安全的做法。把这一点讲清楚,整个项目的技术深度会立刻提升一个档次。
本文还有配套的精品资源,点击获取