ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SSM+Maven+JSP律师管理系统:从数据库设计到权限控制全解析

SSM+Maven+JSP律师管理系统:从数据库设计到权限控制全解析 这个选题一看就很典型javaweb SSM Maven JSP MySQL组合的律师事务所律师管理系统。说句实在话这些年我帮人看的毕设、课设项目里这种带“律所”“律师”“案件管理”关键词的SSM项目出现的频率相当高因为律所业务的实体关系清晰律师、客户、案件、开庭记录权限角色也分明特别适合用来展示SSM框架整合和JavaWeb基本功。今天我就拿这个项目当例子把从技术选型、数据库设计到框架整合、核心模块实现、再到日常开发中那些坑完整拆一遍。你要是正准备做类似的SSM管理系统或者正在纠结这个项目该怎么下手这篇文章可以直接当你的参考地图。1. 项目整体设计与技术选型解析1.1 为什么是SSMMavenJSP从项目需求反推技术选择很多人拿到这个标题第一反应是“怎么还在用JSP怎么不用SpringBoot”这个问题我几乎每次都会被问到。我的回答是一样的你是做毕业设计、课程设计还是在做企业级生产项目如果是为了学习、考试、毕设展示SSM JSP这套组合恰恰是当前JavaWeb教学里最能体现“底层功底”的配置。为什么因为SpringBoot把整合细节都封装掉了SpringBoot项目里你写几个注解框架就跑起来了但这些框架之间是怎么协作的、Spring容器和SpringMVC容器分别管什么、MyBatis的SqlSessionFactory是怎么挂到Spring里的你很难有直观感受。而SSM手写整合的这套过程本质上是把SpringBoot帮我们做的事情手撸一遍——这个过程本身就是面试官最爱问的“SSM整合原理”的实战版答案。再回到律所这个场景。律所管理系统需要做什么表面上就是律师信息维护、案件台账管理、客户信息登记、开庭信息记录、统计分析看板外加不同角色的登录权限控制。这类系统的特点是业务逻辑不算复杂但CRUD操作面广、表间关系清晰、查询条件多。它不像电商系统那样需要海量并发设计也不像推荐系统那样有算法层面的复杂度它考察的重点就是常规业务的建模能力、SSM分层的规范性、数据库设计的合理性。这套技术栈选得非常稳。Maven在这个项目里的角色也值得说一句。很多同学最初接触Maven只知道它能下载jar包其实Maven的核心价值是“约定优于配置”的项目结构管理 依赖版本统一管理 一键打包部署。用Maven管理SSM项目最直接的好处是你不需要再手工往WEB-INF/lib下拷贝一堆乱七八糟的jar包。你只要在pom.xml里声明依赖Maven会把jar包连同它的间接依赖一起拉下来。项目结构固定了以后换机器、换队友git clone下来执行mvn clean install就能跑这才是工程化的基本素养。1.2 系统功能模块拆解与核心业务流程把标题拆开看律所管理系统本质上要服务两类角色管理员和律师。管理员管全局律师管自己的案件。围绕这两类角色系统核心模块可以拆成这样几块登录与权限控制基于session实现登录校验管理员和律师在登录后看到不同的操作菜单未登录用户拦截跳转登录页。律师管理模块管理员对律师信息做增删改查包括姓名、性别、执业证号、专业领域、从业年限、联系方式等字段。客户管理模块登记和管理客户信息客户是与案件关联的当事人主体。案件管理模块案件是整个系统的核心包含案件编号、案件名称、所属客户、承办律师、案件类型、受理状态收案/承办中/已结案、受理时间、结案时间等。管理员可以给案件指派律师律师可以更新自己名下案件的进展。开庭记录模块每个案件关联多条开庭信息记录开庭日期、法庭地点、承办法官、备注等支持按日期范围查询。统计看板首页展示律师数量、案件总数、进行中案件数、已结案件数以及每位律师名下的案件数量排行。这套模块梳理下来数据库的表结构就基本有谱了后面我会把建表SQL拆开详细讲。2. 数据库设计与核心表结构2.1 业务实体分析从“人”到“案件”的关系建模数据库设计是整个系统的地基。我见过太多SSM项目最后挂在数据库设计上——要么字段设计冗余要么表间关系混乱要么类型选得不对导致查询效率低。律所系统里的核心实体其实就那几个用户User、律师Lawyer、客户Client、案件Case、开庭记录CourtSession外加一个可选的案由分类表CaseType。实体关系要理清用户表和律师表是什么关系这里建议用“用户表存账号、律师表存个人信息、一对一关联”的方式而不是把登录账号字段直接塞进律师表。原因是角色扩展性好——以后如果系统里要加前台客服、行政财务这些角色只要在用户表加一个role字段就行不需要改律师表。而且密码字段从业务表中剥离出去数据安全边界更清晰。案件表和律师、客户的关系一个客户可以委托多个案件一个律师可以承办多个案件一个案件必须归属一个客户和一个承办律师。这就是典型的多对一关系。在案件表里保存client_id和lawyer_id两个外键字段是最直接的建模方式。这里不建议做中间关联表因为案件和律师是“承办”关系而不是多对多的“协作”关系中间表反而增加查询复杂度。开庭记录和案件的关系一个案件可能有多次开庭一审、二审、再审或者庭前调解每次开庭记录都挂在案件之下。这是一对多关系在开庭记录表里保留case_id外键即可。2.2 核心建表SQL与字段设计要点我拿案件表和律师表举个例子把关键设计说明写在注释里。律师表CREATE TABLE lawyer ( id INT NOT NULL AUTO_INCREMENT COMMENT 主键, user_id INT DEFAULT NULL COMMENT 关联用户表ID, name VARCHAR(50) NOT NULL COMMENT 律师姓名, gender CHAR(1) DEFAULT 1 COMMENT 性别 1男 2女, practice_no VARCHAR(50) DEFAULT NULL COMMENT 执业证号, specialty VARCHAR(100) DEFAULT NULL COMMENT 专业领域, years INT DEFAULT 0 COMMENT 从业年限, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, status TINYINT DEFAULT 1 COMMENT 状态 1在职 0离职, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT律师信息表;案件表CREATE TABLE legal_case ( id INT NOT NULL AUTO_INCREMENT COMMENT 主键, case_no VARCHAR(50) NOT NULL COMMENT 案件编号, case_name VARCHAR(200) NOT NULL COMMENT 案件名称, client_id INT DEFAULT NULL COMMENT 客户ID, lawyer_id INT DEFAULT NULL COMMENT 承办律师ID, case_type VARCHAR(50) DEFAULT NULL COMMENT 案件类型, status TINYINT DEFAULT 1 COMMENT 状态 1收案 2承办中 3已结案, accept_time DATETIME DEFAULT NULL COMMENT 受理时间, close_time DATETIME DEFAULT NULL COMMENT 结案时间, description TEXT COMMENT 案件描述, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_case_no (case_no), KEY idx_client_id (client_id), KEY idx_lawyer_id (lawyer_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT案件信息表;字段设计的几个细节要特别说明第一状态字段不要用varchar存“进行中”“已结案”这种中文用TINYINT存数字在业务层用常量或枚举去映射。原因很简单varchar做条件查询时要精确匹配中文容易因为空格、全角半角问题出bug而且存储空间更大。数字状态配上页面字典翻译是标准做法。第二时间字段的类型选择。受理时间用DATETIME足够不需要TIMESTAMP的时区换算特性。创建时间设默认值CURRENT_TIMESTAMP这样插入数据时可以少写一个字段让数据库自动维护。第三冗余字段的取舍。案件表里没有直接存客户姓名和律师姓名而是存ID这是合理的。但如果在列表页面要频繁展示客户名和律师名你会反复JOIN两张表。对于这种低并发系统JOIN没问题但如果你懒也可以在案件表冗余一个lawyer_name字段牺牲少量一致性换取查询便利——只要记得更新律师姓名时同步更新冗余字段就行。第四主键类型选INT还是BIGINT这个量级的系统INT完全够用除非你有几十亿数据的预期。但如果你希望表能轻松扩展直接用BIGINT AUTO_INCREMENT也不亏成本和INT差别几乎可以忽略。3. 项目搭建与SSM框架整合实操3.1 Maven项目结构创建与pom.xml核心依赖清单SSM项目的目录结构有固定套路。你可能会想为什么非要用这种结构其实这是Maven的“约定优于配置”SpringBoot也沿用同一套结构所以现在养成习惯以后转型成本很低。lawyer-system/ ├── pom.xml └── src/main/ ├── java/com/lawyer/ │ ├── controller/ │ ├── service/ │ ├── mapper/ │ ├── pojo/ │ └── common/ ├── resources/ │ ├── jdbc.properties │ ├── spring-mybatis.xml │ ├── spring-mvc.xml │ └── mybatis-config.xml └── webapp/ ├── WEB-INF/ │ ├── web.xml │ └── jsp/ └── static/pom.xml里核心依赖就那么几个组我给你理一下思路你对照去配就不会乱properties spring.version5.2.8.RELEASE/spring.version mybatis.version3.5.5/mybatis.version mysql.version8.0.21/mysql.version /properties !-- Spring核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency !-- SpringMVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- MyBatis与Spring整合 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.6/version /dependency !-- 数据库驱动与连接池 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version${mysql.version}/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.1.23/version /dependency !-- JSP相关 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency /xml几个容易踩的坑提前说Spring和JDK版本要匹配。如果用JDK1.8Spring 5.2.x没问题如果JDK版本太高Spring 5.x也能兼容到JDK17左右但spring 3/4的旧版本就不行了。很多同学项目跑不起来不是代码的问题是JDK版本和框架版本不兼容。mysql-connector-java版本要和数据库匹配。MySQL 5.7用8.x版本的驱动没问题但连接URL里要加useSSLfalse避免SSL警告MySQL 8.x必须用8.0的驱动而且URL里要加serverTimezoneAsia/Shanghai否则报时区错误。别忘加javax.servlet-api和jsp-api但scope要设provided。因为Tomcat容器里自带这两套API你打包时如果把它打进去运行时反而冲突。3.2 三剑客配置详解Spring、SpringMVC、MyBatis各管什么SSM整合的本质是一个“容器嵌套容器”的过程。用一个生活化的比喻来说MyBatis是一间厨房它自己知道怎么做菜SQL映射但它不管食材从哪里来数据源管理也不管做好的菜端给谁请求响应分发。Spring是整家餐厅的后勤总管它管理食材供应链数据源、管厨房人员的排班Bean容器还负责处理“用餐异常”这种突发事件事务管理。SpringMVC是前厅的点餐和传菜员负责接待顾客HTTP请求、根据顾客点单请求URL通知厨房做菜调用处理的方法、把菜端上桌响应视图。配置上三个配置文件各有分工spring-mybatis.xmlSpring根容器 MyBatis整合!-- 扫描Service层和Mapper接口 -- context:component-scan base-packagecom.lawyer.service / !-- 读取数据库连接配置 -- context:property-placeholder locationclasspath:jdbc.properties / !-- 配置Druid数据源 -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName value${jdbc.driver} / property nameurl value${jdbc.url} / property nameusername value${jdbc.username} / property namepassword value${jdbc.password} / /bean !-- 配置SqlSessionFactory -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource / property nameconfigLocation valueclasspath:mybatis-config.xml / property namemapperLocations valueclasspath:mapper/*.xml / /bean !-- 扫描Mapper接口 -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.lawyer.mapper / /bean !-- 事务管理器 -- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource / /bean tx:annotation-driven transaction-managertransactionManager /spring-mvc.xmlSpringMVC子容器!-- 只扫描Controller层 -- context:component-scan base-packagecom.lawyer.controller / !-- 注解驱动 -- mvc:annotation-driven / !-- 静态资源放行 -- mvc:default-servlet-handler / !-- 视图解析器 -- bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/jsp/ / property namesuffix value.jsp / /beanmybatis-config.xmlconfiguration settings setting namemapUnderscoreToCamelCase valuetrue/ /settings typeAliases package namecom.lawyer.pojo/ /typeAliases /configuration这里面有几个细节值得说道为什么Controller单独放一个子容器Service和Mapper放根容器这是SpringMVC官方推荐的配置方式。根容器管全局Service、Mapper、数据源子容器只管Web层Controller。如果你把所有东西都丢给SpringMVC扫描你会在Service里无法注入Mapper或者在Controller里扫描到Service导致事务失效各种诡异问题都会冒出来。mapUnderscoreToCamelCase设为true之后数据库字段accept_time可以自动映射到pojo属性acceptTime不用写一堆resultMap。你只要保证pojo属性命名遵循驼峰规范就行。MapperScannerConfigurer扫的是Mapper接口所在的包不是XML文件路径。XML文件的位置由mapperLocations指定这两者要配合好。建议把XML放到resources/mapper目录下这样打包时才能一起进classpath。3.3 web.xml整合与项目打包部署web.xml是SSM项目的总入口核心配置是三个东西ContextLoaderListener启动Spring根容器。DispatcherServlet启动SpringMVC子容器配置init-param指定spring-mvc.xml位置。CharacterEncodingFilter字符编码过滤器解决POST请求乱码问题。web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 display-namelawyer-system/display-name context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mybatis.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping /web-app一个常见误区是DispatcherServlet的url-pattern用/*还是/。注意一定要用/不要用/*。用/*表示拦截所有请求包括JSP和静态资源你会发现自己写的JSP根本没有被视图解析器处理直接返回了源码。用/则会交给SpringMVC处理除JSP之外的请求配合spring-mvc.xml里的静态资源放行CSS、JS、图片都能正常访问。项目的运行方式有两种一种是直接用IDEA的Tomcat插件运行点一下就能看到效果另一种是Maven打包成war丢到Tomcat的webapps目录下。打包前记得在pom.xml里设置packagingwar/packaging build finalNamelawyer-system/finalName plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId configuration source1.8/source target1.8/target /configuration /plugin /plugins !-- 关键让resources下的xml/mapper文件也能被打包 -- resources resource directorysrc/main/resources/directory includes include**/*.xml/include include**/*.properties/include /includes /resource /resources /build这个maven-compiler-plugin的source/target配置能解决一个经典问题IDEA里项目语言级别老是自动变回Java 1.5代码里一写Autowired就报错。根源就是Maven编译器插件默认用的老JDK语法级别你在pom里锁定1.8就一劳永逸。4. 核心业务模块设计与实现4.1 律师管理模块CRUD与条件查询的组合玩法律师管理模块是标准的单表CRUD看起来简单但有两个细节值得展开条件查询和删除保护。条件查询的场景管理员要按照“姓名”“专业领域”“从业年限范围”组合筛选律师列表。MyBatis里写动态SQL是基本功select idfindByCondition resultTypecom.lawyer.pojo.Lawyer SELECT * FROM lawyer where if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if if testspecialty ! null and specialty ! AND specialty #{specialty} /if if testminYears ! null AND years gt; #{minYears} /if if testmaxYears ! null AND years lt; #{maxYears} /if /where ORDER BY id DESC /selectwhere标签会自动去掉第一条多余的AND这是一个很容易被忽略但极其实用的功能。如果你不用where手写WHERE 11加条件也能行但where更规范。删除保护的思路如果一个律师名下已经关联了案件管理员点击删除时应该怎么处理两种方案一种是删除前检查案件表里有没有该律师的lawyer_id有就提示“该律师名下存在案件不能删除”另一种是逻辑删除律师表的status字段从1改成0表示离职/停用数据还保留在库里。我更推荐第二种因为律所场景下历史案件记录必须可追溯物理删了律师以前他承办的案件记录就没法查了。4.2 案件管理模块状态机设计与律师分配逻辑案件管理是整个系统的重头戏它牵扯的查询最多、业务逻辑也最复杂。核心设计是一个状态流转模型状态值状态含义下一步可流转状态1收案待分配2承办中2承办中3已结案3已结案无案件创建的流程是管理员登记案件信息包括客户、案由、描述此时status1。接着管理员在案件列表里点击“指派律师”选择律所里某一律师status自动变2。律师登录后可以在“我的案件”里看到被指派给自己的案件并更新案件进展或标记结案。这里要注意一个边界结案操作必须校验操作者身份。律师只能处理自己名下的案件管理员可以处理所有案件。这个校验可以放在Service层做核心逻辑如下public void closeCase(Integer caseId, Integer operatorUserId) { LegalCase legalCase legalCaseMapper.selectById(caseId); if (legalCase null) { throw new BusinessException(案件不存在); } Integer lawyerUserId lawyerService.getUserIdByLawyerId(legalCase.getLawyerId()); boolean isAdmin userService.isAdmin(operatorUserId); boolean isOwner lawyerUserId ! null lawyerUserId.equals(operatorUserId); if (!isAdmin !isOwner) { throw new BusinessException(没有权限操作该案件); } legalCase.setStatus(3); legalCase.setCloseTime(new Date()); legalCaseMapper.update(legalCase); }这个例子的核心思想是权限校验不能只靠前端隐藏按钮Service层必须做二次校验。前端JSP里按角色渲染按钮只是用户体验层面的优化真正安全的后端逻辑要挡在数据写入之前。4.3 开庭提醒与统计看板日期查询和聚合统计的实现开庭提醒模块和统计看板算是这个项目里稍微有点“亮点”的功能也是最值得在答辩时展示的部分。开庭提醒的实现思路登录律师进入首页后查询开庭记录表里未来7天内自己名下案件的开庭安排。SELECT c.case_name, cs.court_date, cs.court_address, cs.judge FROM court_session cs JOIN legal_case c ON cs.case_id c.id WHERE c.lawyer_id #{lawyerId} AND cs.court_date BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 7 DAY) ORDER BY cs.court_date ASC这个SQL的关键是BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 7 DAY)你不需要在Java层拼日期字符串直接让MySQL算既简洁又避免了时区差异。统计看板相对更简单用聚合函数就能完成-- 各状态案件数量 SELECT status, COUNT(*) AS cnt FROM legal_case GROUP BY status; -- 每位律师承办案件数量排行 SELECT l.name, COUNT(lc.id) AS case_count FROM lawyer l LEFT JOIN legal_case lc ON l.id lc.lawyer_id GROUP BY l.id ORDER BY case_count DESC;一个小的加分项是排行的结果集可以用ECharts在前端画一个柱状图比干巴巴的表格好看得多。虽然做的是JSP项目但引入ECharts只需要一个script标签的事视觉效果拉满答辩时老师一看就知道你有前端意识。4.4 登录权限拦截过滤器实现角色访问控制登录和权限控制部分用一个HandlerInterceptor拦截器比用Servlet Filter更贴合SpringMVC的风格。典型实现思路分三步第一步写一个拦截器类在preHandle方法里获取当前session中的登录用户public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { // 未登录重定向到登录页AJAX请求要特殊处理 response.sendRedirect(request.getContextPath() /login.jsp); return false; } return true; } }第二步在spring-mvc.xml里注册拦截器并配置放行路径mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/ mvc:exclude-mapping path/doLogin/ mvc:exclude-mapping path/static/**/ mvc:exclude-mapping path/css/**/ mvc:exclude-mapping path/js/**/ mvc:exclude-mapping path/images/**/ mvc:exclude-mapping path/login.jsp/ /mvc:interceptor /mvc:interceptors这里有个非常容易踩的坑如果你放宽了登录页和静态资源但没放行Controller层的登录接口路径会出现登录按钮点了没反应的情况因为Ajax请求也被拦截器拦掉了。这个要在配置时仔细对照Controller里定义的RequestMapping来完成。第三步判断角色时用User对象里的role字段装的是1管理员还是2律师在JSP页面用c:if标签判断后决定哪些菜单按钮显示哪些隐藏。千万不要把“判断角色”的逻辑写在SQL里或者写在弹出的JS里拦截器面向URL做粗粒度控制、JSP标签做细粒度按钮渲染这是最干净的职责划分。5. 常见问题与排查技巧实录5.1 典型运行报错速查表这个项目跑的时候报错大头基本集中在几个地方我按出现频率排个序报错场景典型报错信息原因解决方案Maven依赖下载失败Could not resolve dependencies / PKIX path building failed仓库污染、SSL证书、网络问题配置阿里云镜像删除本地仓库中的lastUpdated文件后重新导入数据库连接失败Access denied for user rootlocalhost (using password: YES)MySQL账号密码不对或用了caching_sha2_password加密检查jdbc.properties配置创建用户时用mysql_native_password或升级驱动8.0数据库连接失败Could not create connection to database server / time zone / SSL连接URL缺少参数URL加上?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiMyBatis绑定异常Invalid bound statement (not found)Mapper接口和XML映射文件没有匹配mapperLocations配置检查mapper接口全限定名要和XML的namespace一致项目启动就报错NoSuchBeanDefinitionException包扫描路径不对或配置类没被扫描检查component-scan的base-package是否覆盖了Bean所在包JSP页面空白/样式丢失控制台无报错CSS不生效静态资源配置错了被DispatcherServlet拦截spring-mvc.xml里加mvc:default-servlet-handler/或用/static/**映射中文乱码数据库显示??页面显示乱码编码链路不一致统一UTF-8包括JSP页面charset、MySQL连接URL、数据库表charset、CharacterEncodingFilterIDEA运行Tomcat秒退Application Server was not connected / 端口被占用Tomcat端口被占用或者热部署冲突检查8080端口占用改用其他端口启动这些坑我基本都踩过一遍特别是Maven依赖下载失败这个最常见的原因是本地仓库里残留了下载不完全的jar包Maven检测到*.lastUpdated文件就不重新下载了。解决方法是找到本地仓库路径C:\Users\你的用户名\.m2\repository搜出所有.lastUpdated文件删除然后在IDEA里右键项目Maven - Reimport。如果你还没给Maven配置国内镜像强烈建议在settings.xml里配上阿里云镜像下载速度从龟速变秒开。5.2 数据库设计阶段的隐藏雷区数据库设计阶段有几个坑比运行报错更隐蔽后患无穷值得单独说第一个坑是表名和字段名用了MySQL关键字。比如order、group、desc这些词做字段名写SQL时你以为写对了MySQL直接给你报语法错误。我见过有人把“客户表”命名为client没问题结果字段里加了个desc做备注每条SQL都得写反引号包起来极其别扭。设计表时避开关键字是第一原则。上面我用的表名legal_case就是为了避开case这个MySQL关键字顺便说一句case虽然能做表名但必须加反引号很坑。第二个坑是连接查询里的笛卡尔积风险。比如你查案件列表时JOIN客户表和律师表忘了写JOIN条件会查出案件数乘以客户数的结果集页面数据直接重复。排查方法是在SQL里先单表查一遍看数量再JOIN查一遍对比如果数量暴增基本就是JOIN条件写漏了。第三个坑是索引缺失导致慢查询。律所系统数据量不大可能感受不到差异但如果你建立了唯一索引uk_case_no、普通索引idx_client_id、idx_lawyer_id在答辩时被问到查询性能优化你至少可以说出“外键字段建索引避免回表”这种话这是一个很大的加分点。5.3 毕设演示与答辩的5个实用建议说点实际经验。就算你的系统已经能跑通了演示和答辩也有不少讲究这一套我陪人演练了很多次总结下来最有效的几条演示顺序要按“角色主线”走。先演示管理员登录展示律师管理、案件指派、统计看板再退出换律师账号登录展示律师只能到自己负责的模块、只能看自己名下案件、有未来开庭提醒。用两个角色的对比来体现权限设计这个逻辑最清晰。准备2到3个“亮点说辞”。比如“案件状态流转设计”“Service层权限二次校验”“开庭提醒用NOW()和DATE_ADD避免应用层和数据库时区差异”老师问一个你能反哺一个会显得你是真的理解项目。别把数据清空。演示库里必须预置至少15条律师数据、20条案件数据、若干条开庭记录状态要覆盖收案、承办中、已结案三种这样统计看板数字丰富列表分页也能看得出效果。空荡荡的数据库是演示大忌。准备一个“出错了怎么圆”的备份方案。提前把数据库备份一份演示前如果发现数据被改得乱七八糟直接恢复备份不要手忙脚乱在现场删改数据。提前测试你用来演示的浏览器和分辨率。曾经见过演示现场因为浏览器缩放比例不对页面布局乱掉老师第一印象就差了。用Chrome的无痕模式打开项目能避免插件干扰也显得干净规范。写在最后的个人体会这类SSM管理系统项目做多了你会发现一个规律真正能拉开差距的不是框架有多新而是工程细节有多讲究。同样是律师管理系统有的人做出来就是CRUD堆砌页面粗糙、权限形同虚设、代码全堆在Servlet里有的人做出来是分层清晰、事务到位、权限有兜底、SQL写得出彩。你从这篇文章里拿到手的每一段配置、每一个SQL、每一条避坑记录都是我曾经加班排错排出来的经验照着做至少能让你少走一个月的弯路。如果后续你有余力不妨试着在这个基础上加一点“让项目站起来”的东西比如把统计看板的图表换成ECharts的动态图表给律师端加一个Excel案卷导出或者用Spring Schedule做一个“开庭前三天自动发送提醒邮件”。这些扩展不会改变SSM的技术主框架但会让你的项目在演示时眼前一亮。技术选型可以变工程思维和设计思路是相通的等你真正理解了这个系统里每个模块背后的取舍逻辑也就真正把SSM这套东西学到手了。
返回列表