ARTICLE DETAIL

资讯详情

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

SSM客户资料管理系统毕设实战:框架整合、数据库脚本与避坑指南

SSM客户资料管理系统毕设实战:框架整合、数据库脚本与避坑指南 简介基于SSM框架的客户资料管理系统项目源码包是一套面向计算机相关专业毕设学生和Java学习者的完整课程设计资源可直接用于毕业设计或项目实战训练。整个压缩包内共有3个文件其中源码包内含完整前后端代码sql脚本用于快速初始化MySQL数据库txt项目说明对运行环境和部署步骤做了必要提示包体大小约25.18MB。系统采用Spring、SpringMVC、MyBatis框架前端用JSP实现可在JDK、Eclipse、Tomcat环境下运行围绕管理员、客户经理、营销主管三类角色提供员工信息、产品信息、客户信息、服务信息、交易信息、基础信息查询等模块化功能界面清晰操作直观。已有1112人浏览学习适合需要快速搭建CRM系统原型、理解SSM整合流程或准备毕设答辩演示的读者。1. 用 SSM 做客户资料管理系统毕设选题为什么依旧能打临近毕设的节点「客户资料管理系统」总被当成老掉牙的题目但正是这种看似普通的题目最能检验一个人能不能把一个完整项目从数据库脚本跑到浏览器页面。SSM 这套组合——Spring 管对象和事务、SpringMVC 管请求路由、MyBatis 管 SQL 映射——到今天仍然是中小型管理系统的主流后端结构。这个题目能解决的是从零搭起一套可演示的客户档案增删改查附带可重复执行的数据库脚本让你在答辩时既拿得出源码也讲得清配置。适合 Java 基础一般、需要快速交付完整项目的同学也适合想把 Spring 容器和 MyBatis 原理讲明白的技术型选手。这篇笔记就按选型、建库、整合、避坑、加分的顺序把整套落地路径摊开讲。2. SSM 客户资料管理系统的框架选型Spring、SpringMVC、MyBatis 各管哪一块2.1 为什么客户资料这种业务适合用 SSM 复现客户资料管理系统的核心是单表为主、外键少、查询条件多按姓名模糊查、按行业过滤、按客户等级筛选、跟进状态流转。这些需求放到 SSM 里正好对应三个层面的分工Spring 容器负责把 Service、Mapper 这些对象组装起来并通过声明式事务保证「新增客户」这类操作要么成功要么回滚SpringMVC 把浏览器的表单请求映射到 Java 方法参数自动绑定到 POJOMyBatis 则把 SQL 写在 XML 里动态条件可以按参数拼装避免写死 SQL。对毕设来说这种分工还有个额外好处每个环节都能对应一个运行机制问题比如「请求是怎么从 JSP 到数据库的」「事务注解为什么有时不生效」这些是答辩老师最常问的点。有人会问现在都用 Spring Boot 了SSM 是不是过时我的看法是Spring Boot 把配置自动完成项目能跑但讲不透SSM 虽然 XML 多一些但每一个配置都能在启动过程里找到对应位置反而更适合拿来展示基本功。如果你目标是快速出活Spring Boot 更香但标题既然锁在 SSM这篇笔记就把 SSM 的整条链路讲清楚让你在「能跑」之外还能讲明白。2.2 一次「新增客户」请求是如何穿过 SSM 三层的别急着写代码先在脑子里把这条链路立住。用户在 JSP 页面填好表单点提交浏览器发一个 POST 请求到/customer/add。请求先进 Tomcat送到 DispatcherServlet——SpringMVC 的前端控制器。DispatcherServlet 根据 URL 找到 CustomerController 里标注了PostMapping(/add)的方法并把表单字段customerName、contactPhone、email…自动封装成 Customer 对象。Controller 不直接碰数据库它调用 Service 接口Service 实现类加上Transactional后通过 CustomerMapper 接口去操作 t_customer 表。MyBatis 在这里做的事是读取 resources/mapper 下的 XML 映射文件把接口方法名和 SQL 对应起来执行后把结果集映射回 List。返回时 Controller 返回一个逻辑视图名视图解析器拼出/WEB-INF/views/customer/list.jspTomcat 渲染完页面回给浏览器。这条链路里最容易理解错误的是Controller 和 Service 都是普通的 Java Bean靠 Spring 容器管理你不需要用new去创建它们。Autowired会把容器里已经初始化好的对象注入进来如果注入失败启动时就会报NoSuchBeanDefinitionException这时候先检查包扫描路径是不是覆盖到了对应包。2.3 依赖坐标与版本搭配一份能直接翻建项目的 pom.xmlSSM 项目本质上是一个 Maven Web 项目。我一般会固定 Spring 5.2.x、MyBatis 3.5.x、MySQL 8.0.x这几个版本搭配成熟网上能找到大量兼容案例。pom.xml 里这些依赖缺一不可尤其不要漏掉 mybatis-spring它是 MyBatis 和 Spring 之间的桥梁。properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target spring.version5.2.20.RELEASE/spring.version mybatis.version3.5.9/mybatis.version /properties dependencies !-- Spring MVC 会连带引 Spring 核心容器 -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- 事务管理需要 DataSourceTransactionManager -- dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency !-- MySQL 8 必须用 com.mysql.cj.jdbc.Driver -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.28/version /dependency !-- Druid 连接池参数比默认连接池直观 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency /dependencies依赖里需要留意的是scopeprovided的 servlet-apiTomcat 本身已经有 Servlet 容器实现如果打成 war 包时把这个依赖也带进去不同版本的 Servlet 类会冲突启动时可能报NoSuchMethodError。spring-jdbc是事务的基础mybatis-spring负责把 MyBatis 的 SqlSessionFactory 交到 Spring 容器里并让 Mapper 接口能被自动注册。JDK 版本用 1.8Tomcat 用 8.5 或 9不要用 Tomcat 10因为 Tomcat 10 的 Jakarta 命名空间和 SSM 这套 javax.* 依赖默认不兼容那种翻车现场我见过不止一次。3. 从数据库脚本开始落地建表、初始化数据与 IDEA 导入脚本的边界坑3.1 客户资料表的字段设计与规范化客户资料表的字段设计决定了后续代码量。最怕的就是把一堆备注、地址、联系人堆在一张表里造出一张几十列的宽表。对一个毕设来说字段在 8 到 12 列之间最合适既能覆盖常见需求又能在答辩时快速讲清每个字段的作用。我推荐的初始化脚本如下CREATE DATABASE IF NOT EXISTS customer_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE customer_db; DROP TABLE IF EXISTS t_customer; CREATE TABLE t_customer ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, customer_name VARCHAR(64) NOT NULL COMMENT 客户名称, contact_phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, email VARCHAR(128) DEFAULT NULL COMMENT 邮箱, industry VARCHAR(32) DEFAULT NULL COMMENT 所属行业, level TINYINT NOT NULL DEFAULT 1 COMMENT 客户等级 1~5, follow_status TINYINT NOT NULL DEFAULT 0 COMMENT 跟进状态 0未跟进 1跟进中 2已成交 3已流失, remark VARCHAR(500) DEFAULT NULL COMMENT 备注, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_contact_phone (contact_phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT客户资料表; INSERT INTO t_customer (customer_name, contact_phone, email, industry, level, follow_status, remark) VALUES (测试客户A, 13800138000, testaexample.com, 制造, 3, 1, 重点跟进中), (测试客户B, 13900139000, testbexample.com, 软件, 2, 0, NULL);几个字段选型理由可以当答辩素材电话为什么用 VARCHAR 而不是 BIGINT因为电话号码可能出现分机号、以 0 开头数字类型会把前导零丢掉邮箱长度不同VARCHAR 比 CHAR 省空间level 用 TINYINT 而不是 VARCHAR 存「高、中、低」是为了排序方便MySQL 按数字排序比按中文字符串排序可靠。create_time 由数据库生成update_time 用ON UPDATE CURRENT_TIMESTAMP自动更新这样演示「修改客户资料」时只需要改 update_time 字段就能说明数据在变化不用手动维护时间。如果你的 MySQL 版本是 5.6 之前DATETIME 不支持DEFAULT CURRENT_TIMESTAMP这种写法会直接语法报错。遇到这种情况把 create_time 和 update_time 改成 TIMESTAMP 类型或者直接在 Java 层 new Date() 再 set 进去。既然面向现代部署环境优先 MySQL 8.0 最省事。3.2 用 IDEA 执行数据库脚本先建库再建表不要全选直接 Run拿到项目源码里的数据库脚本文件后不要上来就选中整个文件按运行那样一条报错会让后面脚本全部停在原地。正确顺序是在 IDEA 右侧 Database 面板新建一个 DataSource选择 MySQLURL 写jdbc:mysql://localhost:3306/customer_db?useSSLfalseserverTimezoneAsia/Shanghai用户名密码按本机配置填先把连接建好。然后右键这个数据源选择 Open Console在 Console 里执行CREATE DATABASE IF NOT EXISTS ...再打开 init_db.sql只选中建表和插入语句部分执行。命令行方式也适用Linux 或 Windows 终端里执行mysql -uroot -p init_db.sql执行完用这条 SQL 确认表和数据都到位SELECT COUNT(*) FROM t_customer;如果返回的不是 0说明初始化数据写入成功。这里最容易犯的「小白操作」是脚本开头没有USE customer_db在数据库 Console 里执行时会把表建到默认的 information_schema 或者某个旧库里去。所以脚本第一段必须显式声明库名这也是我在项目源码里看到的最常见的交付缺陷。3.3 数据库脚本导出的几个坑从「本机能跑」到「对方电脑能跑」毕设要交付的是整个项目源码 数据库脚本这里的数据库脚本不只是你本机导出的那一份 dump而是一份能在空环境里重复执行的初始化脚本。IDEA 导出数据库脚本是个高频动作但直接导出常常会踩到三个边界第一导出的 SQL 文件可能不包含 CREATE DATABASE。你自己的连接本来就在 customer_db 里导出的表语句都是CREATE TABLE t_customer对方新建连接时不知道该切到哪个库。解决方法是导出后检查文件头部没有CREATE DATABASE就手动补上或者直接用我们上面写的 init_db.sql 作为唯一交付脚本。第二编码问题。Windows 上用 IDEA 导出时如果 File Encoding 是 GBK导出的 SQL 里中文备注会变成乱码。解决方法是导出前把脚本右下角编码切到 UTF-8并且导出后重新执行一次再 SELECT 查一下中文是否正常。数据库脚本里中文字段名尽量避免表和字段名统一英文注释可以保留中文但前提是整个链路编码一致。第三约束丢失。只导出数据时唯一索引、外键这些结构信息不会出现在 INSERT 语句里。所以一定要确认导出选项里选了「Structure Data」或「Schema with Data」。否则你在本机测试唯一约束没问题交到对方电脑上插入了重复手机号也不报错答辩现场老师一看就露馅了。我的建议是不让 IDE 导出的全量脚本当唯一的交付物而是维护一份手写的 init_db.sql里面把结构、约束、初始化数据、注释都控制在自己手里。4. 把项目跑起来SSM 整合配置与客户资料管理的最小 CRUD4.1 包规划与三层结构Controller、Service、Mapper 各归其位SSM 项目的第一件事不是写代码而是把包结构定下来。包结构乱的项目等配置文件写好之后常常出现「Mapper 扫描不到」「Controller 注入失败」这类玄学问题其实都是包路径没对上。我习惯这样搭src/main/java/com/example/customer/ ├── controller/CustomerController.java ├── service/CustomerService.java ├── service/impl/CustomerServiceImpl.java ├── mapper/CustomerMapper.java └── pojo/Customer.java src/main/resources/ ├── applicationContext.xml ├── spring-mvc.xml ├── mybatis-config.xml ├── jdbc.properties └── mapper/CustomerMapper.xml注意一个关键点CustomerMapper.java 接口在com.example.customer.mapper包下而 CustomerMapper.xml 放在 resources 下的mapper/目录。这种放法能让 XML 在编译后留在 classes 根目录的 mapper 里MyBatis 用classpath:mapper/*.xml就能扫到。如果你想更规范可以把 XML 和接口放在同一个包路径下但那样需要在 pom.xml 里额外配置 resources 包含 xml 文件否则打包后 XML 丢失。对新手来说XML 统一放 resources/mapper 是最不容易出错的方案。POJO 的字段类型要和数据库列对齐BigDecimal 对应 DECIMALString 对应 VARCHARInteger 对应 TINYINTLocalDateTime 对应 DATETIME。如果 Java 侧用 LocalDateTimeMyBatis 3.5.x 无需额外配置就能映射如果你项目里用的还是 java.util.Date也问题不大只是打印日志时没那么直观。4.2 三份配置文件的职责边界Spring 容器、SpringMVC 容器和 MyBatis 设置SSM 里有三份 XML很多人搞不清为什么要拆开。applicationContext.xml 管全项目共有对象Service、数据源、事务、MyBatis 的 SqlSessionFactoryspring-mvc.xml 只管 Controller 和请求相关组件mybatis-config.xml 只放 MyBatis 的全局设置比如驼峰映射和日志。这样拆的目的是避免 Controller 被 Spring 容器和 SpringMVC 容器重复扫描重复扫描会导致事务注解失效这是 SSM 整合的一个典型雷区。applicationContext.xml 的核心配置!-- 扫描 Service 层实现类排除 Controller -- context:component-scan base-packagecom.example.customer context:exclude-filter typeannotation expressionorg.springframework.stereotype.Controller/ /context:component-scan context:property-placeholder locationclasspath:jdbc.properties/ 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}/ property nameinitialSize value2/ property namemaxActive value10/ /bean !-- 把 MyBatis 的 SqlSessionFactory 交给 Spring 管理 -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property nameconfigLocation valueclasspath:mybatis-config.xml/ property namemapperLocations valueclasspath:mapper/*.xml/ property nametypeAliasesPackage valuecom.example.customer.pojo/ /bean !-- 扫描 Mapper 接口自动生成实现 -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.example.customer.mapper/ property namesqlSessionFactoryBeanName valuesqlSessionFactory/ /bean bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertransactionManager/typeAliasesPackage的作用是让 XML 里写resultTypeCustomer而不是写一大串全限定类名。MapperScannerConfigurer会把接口统一注册进 Spring 容器之后你在 Controller 里直接Autowired指定的接口名即可。tx:annotation-driven是Transactional生效的前提少了这一行后面写了事务注解也不会有回滚行为。spring-mvc.xml 要单独扫描 controller 包并配置视图解析器context:component-scan base-packagecom.example.customer.controller/ mvc:annotation-driven/ bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean mvc:default-servlet-handler/视图解析器把 Controller 返回的customer/list解析成/WEB-INF/views/customer/list.jsp。mvc:default-servlet-handler保证静态资源如 CSS、JS 能找到 Tomcat 的默认 Servlet否则样式会全部失效。mybatis-config.xml 保持精简configuration settings setting namemapUnderscoreToCamelCase valuetrue/ setting namelogImpl valueSTDOUT_LOGGING/ /settings /configurationmapUnderscoreToCamelCase开启后数据库列 customer_name 会自动映射到 Java 属性 customerName不用每个字段写 resultMap。STDOUT_LOGGING会把 SQL 语句和执行结果打印到控制台调试时看它比看业务日志快得多。4.3 实现客户新增与列表查询Controller、Service、Mapper 三层联动配置齐了代码可以从最小闭环开始写。先写 CustomerController只做两件事接收新增表单、查列表。Controller RequestMapping(/customer) public class CustomerController { Autowired private CustomerService customerService; PostMapping(/add) public String add(Customer customer) { customerService.addCustomer(customer); return redirect:/customer/list; } GetMapping(/list) public String list(RequestParam(defaultValue ) String customerName, Model model) { model.addAttribute(list, customerService.queryCustomer(customerName)); return customer/list; } }PostMapping(/add)是 Spring 4.3 之后的组合注解等价于RequestMapping(value/add, methodPOST)。方法参数直接写 CustomerSpringMVC 会把 request 里的参数按字段名绑定到对象上省去手写request.getParameter的重复劳动。新增成功之后用 redirect 重定向到列表页避免刷新页面时重复提交表单。Service 接口和实现public interface CustomerService { int addCustomer(Customer customer); ListCustomer queryCustomer(String customerName); }Service public class CustomerServiceImpl implements CustomerService { Autowired private CustomerMapper customerMapper; Override Transactional public int addCustomer(Customer customer) { return customerMapper.insertCustomer(customer); } Override public ListCustomer queryCustomer(String customerName) { return customerMapper.selectByCondition(customerName); } }Transactional里不写任何参数时默认对运行时异常回滚。如果你在方法里抛了一个RuntimeExceptionSpring 会回滚如果抛的是受检异常默认不回滚。答辩时这个区别经常被追问最好记住。Mapper 接口则要配 XML 才有实际 SQLpublic interface CustomerMapper { int insertCustomer(Customer customer); ListCustomer selectByCondition(Param(customerName) String customerName); }insert idinsertCustomer useGeneratedKeystrue keyPropertyid INSERT INTO t_customer (customer_name, contact_phone, email, industry, level, follow_status, remark) VALUES (#{customerName}, #{contactPhone}, #{email}, #{industry}, #{level}, #{followStatus}, #{remark}) /insert select idselectByCondition resultTypeCustomer SELECT id, customer_name, contact_phone, email, industry, level, follow_status, remark, create_time, update_time FROM t_customer where if testcustomerName ! null and customerName ! AND customer_name LIKE CONCAT(%, #{customerName}, %) /if /where ORDER BY update_time DESC /selectuseGeneratedKeystrue会在执行 INSERT 后把自增主键回填到 Customer 对象的 id 属性方便在新增完成后立刻拿到新客户 ID。where标签会自动处理第一个条件前面的 AND如果 customerName 为空它生成的 SQL 不会带 WHERE整个列表就能查出来。LIKE CONCAT(%, #{customerName}, %)是参数化查询没有字符串拼接导致 SQL 注入的风险这里不要图省事写成%${customerName}%后者会把参数拼进 SQL是明显的注入漏洞答辩时被点名要扣分的。5. 毕设答辩与自测避坑中文乱码、事务失效、部署翻车5.1 插入到数据库的中文全变成「??」四级字符集排查链现象页面上填的「测试客户」保存后数据库里显示的是「??」。原因不是某一处编码问题而是页面响应编码、请求传输编码、JDBC 连接编码、数据库表编码四级里至少有一级不是 UTF-8。解决时按这条链从外到内检查JSP 顶部加上pageEncodingUTF-8web.xml 里配置 CharacterEncodingFilter 强制 request 和 response 都用 UTF-8JDBC URL 末尾加characterEncodingutf8数据库连接和表结构确认是utf8mb4。这四级里漏掉任何一级中文乱码都会以不同形式冒出来。具体到 SSM 项目里最常见的是只配了数据库没配 Tomcat 层。如果你的请求是 GETTomcat 8.5 默认 URI 编码是 UTF-8问题还不明显如果是 POST 表单没有过滤器时 request 默认按 ISO-8859-1 解码中文必然乱。加入 CharacterEncodingFilter 后绝大多数乱码都能拦住。5.2 加了 Transactional 异常后数据还是写进去了现象Service 方法先是customerMapper.insertCustomer(customer)然后手动抛一个RuntimeException按预期应该回滚但数据库里客户记录还在。原因通常是三选一applicationContext.xml 里没有写tx:annotation-driven或者事务注解写在了接口方法上而实现类里没有Spring 的注解扫描默认只认实现类或者同一个类内部调用事务方法导致事务没经过 Spring 的处理边界。这里最隐蔽的是第三种类内部调用不会经过 Spring 生成的增强对象事务注解形同虚设。解决方法是确认 Spring 容器配置了事务驱动把Transactional写在实现类方法上需要自调用时把事务逻辑拆到另一个 Bean 里或者用AopContext.currentProxy()不过毕设场景我更建议直接拆类。验证回滚的方式是在方法末尾故意除以零抛异常然后在数据库里查这条记录如果不存在事务生效。5.3 部署后列表页能打开提交表单却 404现象直接访问/customer/list能看到页面但表单提交到/customer/add报 404。原因一表单里的 action 写成了/customer/add但项目部署名是customer-manage实际访问路径应该是http://localhost:8080/customer-manage/customer/add少了项目上下文路径自然 404。原因二DispatcherServlet 在 web.xml 里映射成了/*把所有请求包括 JSP 都拦截下来转发到视图解析器后找不到对应的 Controller导致 404 或 405。原因三表单提交方式写成了 GET而后台方法是 POST会报 405。解决方法是在 JSP 里统一用${pageContext.request.contextPath}拼请求地址web.xml 中把 DispatcherServlet 映射路径写为/而不是/*提交方式用form methodpost不要因为懒而省略。这三条做到后404 基本能消灭。5.4 IDEA 导出数据库脚本后再导入唯一约束丢失现象本机执行 init_db.sql 一切正常但把 IDEA 导出的脚本给对方后插入重复手机号居然不报错。原因是 IDEA 数据库面板的导出选项默认是只导出数据结构里的 PRIMARY KEY、UNIQUE KEY 没有一并导出或者导出的 SQL 里没有切到目标库把表建到了搞错的地方。解决方法是导出时选择 Schema with Data导完打开文本搜一下UNIQUE和CREATE TABLE确认约束存在。更稳的做法是不要让 IDE 的导出结果替代手写脚本手写脚本可以做到每条 SQL 都可控IDE 导出只作为备份。这条经验在我做项目源码交付时帮了很大的忙研发出机环境下数据库结构不一致八成都是这个环节出的问题。5.5 项目源码压缩包发过去后对方启动失败现象你本机能跑同学导入后却报「Could not resolve javax.servlet-api」或者「Unknown database customer_db」。原因多半是三件事对方本地 Maven 仓库缺依赖网络又不通数据库账号密码不是你写在 jdbc.properties 里的那套还有可能是对方 JDK 是 17而你用的是 1.8导致编译失败。解决方法是pom.xml 里明确定死版本不要用变量悬空jdbc.properties 里用注释标明「改成你自己的库名和密码」项目里附带一份 README 或者部署说明文档写明 JDK 8、Tomcat 8.5/9、MySQL 8、Maven 3.6 这几项前置环境。这些东西不会增加太多工作量但直接决定别人照着你的项目源码能不能复现毕设评审时对方跑不起来比功能少写两项还难评。6. 把项目做成能答辩的加分项客户资料检索的进阶实现与验证技巧6.1 给列表查询加一个真正可用的组合分页前面写的是单个条件模糊查询答辩时如果要加亮点建议叠加「多条件组合查询 分页」这是客户资料管理系统最贴近真实场景的需求。Controller 接收 pageNum 和 pageSize把参数传给 MapperSQL 里用LIMIT #{offset}, #{size}做物理分页select idselectPage resultTypeCustomer SELECT id, customer_name, contact_phone, email, industry, level, follow_status, create_time, update_time FROM t_customer where if testcustomerName ! null and customerName ! AND customer_name LIKE CONCAT(%, #{customerName}, %) /if if testindustry ! null and industry ! AND industry #{industry} /if if testlevel ! null AND level #{level} /if /where ORDER BY update_time DESC LIMIT #{offset}, #{size} /select分页参数的计算公式offset (pageNum - 1) * pageSizepageNum 从 1 开始。为什么不直接用 PageHelperPageHelper 在 SSM 里也能用但它的分页拦截器对老项目有时候会吞掉 count 查询出现页面上显示总条数不对。手写 LIMIT 的代码量不大更适合毕设这种需要把原理讲清楚的项目。6.2 自测时别只点页面用命令和 SQL 快速做回归页面能点通不代表边界条件没问题。我建议答辩前做一轮命令级验证趁早暴露问题。启动项目后用 curl 发一次新增curl -X POST http://localhost:8080/customer/add \ -d customerName测试客户AcontactPhone13800138000level3再用一条 SQL 确认插入结果SELECT id, customer_name, contact_phone, level FROM t_customer WHERE contact_phone 13800138000;如果返回一条记录说明穿过了 DispatcherServlet、Controller、Service、Mapper 全链路。接着再发一次同样的请求看是否因为唯一约束报错这个报错能证明数据库脚本里的 UNIQUE KEY 没有丢。最后验证事务回滚在 Service 的 addCustomer 方法里临时加一行throw new RuntimeException(rollback check);重新请求后查库如果查不到新记录说明事务配置是通的。验完把这行异常删掉即可。我自己的习惯是每次拿到一套新的项目源码先按「建库 - 导数据 - 启动 - 走一遍 CRUD - 打一个 war 包」的顺序跑通再去想功能扩展。客户资料管理系统这类毕设不需要做得多花哨把数据库脚本、事务回滚、组合查询这三个点做实已经比大多数「只能点通页面」的项目强出不少。别把客户资料表设计成无底洞字段越堆越多最后自己维护不过来答辩被问细节时反而容易翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表