☰
SSM框架实战:从零构建客户管理系统,掌握Java Web开发核心
2026/10/20 8:55:02 网站建设 项目流程

简介:本资源是一套面向计算机专业本科生毕业设计及Java初学者实战训练的客户关系管理(CRM)系统,基于SSM(Spring+SpringMVC+MyBatis)框架开发,覆盖客户资料管理、服务跟踪、交易记录、客户等级与开发进度等核心业务场景。压缩包共3个文件(1个主程序ZIP、1个SQL数据库脚本、1个项目说明TXT),总大小25.18MB,其中SQL脚本可一键初始化MySQL数据结构,TXT文档详述环境配置与运行步骤,主ZIP含完整Eclipse工程源码,支持JDK+Tomcat快速部署。已有1111人学习下载,系统采用分层架构与角色权限控制,内置管理员、客户经理、营销主管三类用户,功能模块清晰、界面简洁、调试完备,可直接用于毕设答辩或Java Web开发能力强化训练。

1. 项目概述:一个典型的SSM毕设项目能教会你什么?

又到了一年一度的毕业季,对于计算机相关专业的同学来说,毕业设计是绕不过去的一道坎。很多同学一听到“毕设”两个字就头疼,感觉无从下手,特别是那些平时实践机会不多的同学。如果你正在为“基于SSM的客户资料管理系统”这类题目发愁,或者想找一个结构清晰、能跑通、还能学到东西的项目来练手,那这篇文章就是为你准备的。

这个项目,说白了,就是一个用Java技术栈实现的、用来管理客户信息的Web系统。它麻雀虽小,五脏俱全,涵盖了企业级Java开发中最经典、最实用的技术组合:SSM框架(Spring + Spring MVC + MyBatis)。为什么说它适合毕设和初学者?因为它需求明确(增删改查客户信息),技术栈成熟稳定,网上资料丰富,而且从数据库设计到前端展示,完整地走一遍流程,能让你对Web开发有一个系统性的认识。你做完它,不仅能交出一份像样的毕业设计,更重要的是,你能真正理解一个Web应用是如何从零到一搭建起来的,这对你后续找工作、做项目有莫大的帮助。

2. 项目整体设计与技术选型思路

2.1 为什么是SSM框架?

在开始动手之前,我们先聊聊为什么选择SSM作为技术栈,而不是更新的Spring Boot。这对于理解项目架构至关重要。

SSM是Spring、Spring MVC和MyBatis三个框架的集成。Spring Boot的出现确实简化了配置,但对于学习而言,SSM能让你更清晰地看到各个组件的职责和它们是如何协作的。Spring Boot帮你把轮子装好了,而SSM需要你自己动手组装轮子,这个过程能加深你对框架原理的理解。

  • Spring:它是整个项目的“大管家”,负责管理所有对象(Bean)的生命周期和依赖关系。比如,我们的Service层需要调用Dao层,这个“调用关系”就是由Spring通过依赖注入(DI)来完成的,而不是我们在代码里new出来的。这样做的好处是解耦,方便测试和维护。
  • Spring MVC:它负责处理Web请求。当用户在浏览器点击一个按钮,发起一个“添加客户”的请求时,这个请求会先到达Spring MVC的前端控制器(DispatcherServlet),然后由它找到对应的控制器(Controller)方法来处理。Controller处理完业务逻辑(通常调用Service),最后返回一个视图(比如JSP页面)或者数据(JSON)给浏览器。它是连接用户操作和后端逻辑的桥梁。
  • MyBatis:它是一个优秀的持久层框架,负责和数据库打交道。我们写SQL语句,MyBatis帮我们执行,并把数据库返回的结果集,自动映射成我们定义好的Java对象。相比于Hibernate的全自动ORM,MyBatis半自动的方式让我们对SQL有完全的控制力,这对于性能优化和复杂查询来说非常有利,也更符合很多国内开发者的习惯。

注意:对于毕设项目,使用SSM而非Spring Boot,在答辩时可能更能体现你对基础框架的理解。你可以清晰地阐述web.xml、Spring配置文件、MyBatis配置文件各自的作用,这比直接说“Spring Boot自动配置”更有技术深度。

2.2 系统核心功能模块拆解

一个客户资料管理系统,核心功能无非是对客户信息的“增删改查”(CRUD)。但一个好的设计,会让这些操作更安全、更高效、更易用。我们通常会将系统划分为以下几个层次:

  1. 表现层(View):用户直接操作的界面。这里我们选择经典的JSP + JSTL + Bootstrap的组合。JSP用于动态生成页面,Bootstrap能让我们的界面快速变得美观且响应式,适配电脑和手机。为什么不直接用HTML?因为我们需要在页面上展示从数据库查出来的动态数据,JSP可以嵌入Java代码片段(虽然不推荐大量使用)或使用JSTL标签库来循环展示客户列表。
  2. 控制层(Controller):接收页面请求,调用业务逻辑,决定返回哪个页面或数据。这是Spring MVC的舞台。每个功能,比如“查询客户列表”、“新增客户”,都会对应Controller中的一个方法。
  3. 业务逻辑层(Service):处理具体的业务规则。比如,在新增客户前,需要检查手机号格式是否合法、邮箱是否重复等。这一层是核心业务逻辑所在,它调用持久层的方法,但封装了更复杂的业务判断。
  4. 持久层(Dao/Mapper):直接操作数据库,执行SQL语句。这一层接口和SQL映射文件(XML)由MyBatis管理。
  5. 实体层(Entity/Model):就是普通的Java类(POJO),用来对应数据库中的一张表。例如Customer类,里面有id、name、phone、email等属性,和数据库t_customer表的字段一一对应。

这种分层架构的好处是职责清晰,任何一层的变化,只要接口不变,对其他层的影响就最小。比如,今天用MySQL,明天想换Oracle,你只需要修改MyBatis的SQL映射和数据库连接配置,Service层以上的代码几乎不用动。

2.3 数据库设计要点

数据库设计是项目的基石,设计得好,后期开发事半功倍。对于客户表,我们至少需要以下字段:

CREATE TABLE `t_customer` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '客户ID(主键)', `name` varchar(50) NOT NULL COMMENT '客户姓名', `gender` tinyint(1) DEFAULT NULL COMMENT '性别:0-女,1-男', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `email` varchar(100) DEFAULT NULL COMMENT '电子邮箱', `company` varchar(200) DEFAULT NULL COMMENT '公司名称', `position` varchar(100) DEFAULT NULL COMMENT '职位', `address` varchar(500) DEFAULT NULL COMMENT '地址', `remark` text COMMENT '备注', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_phone` (`phone`), -- 手机号唯一索引 KEY `idx_name` (`name`) -- 姓名普通索引,方便按姓名搜索 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户信息表';

设计思考:

  • id:主键,自增。这是表的唯一标识。
  • gender:用tinyint存储,比varchar存储“男”、“女”更节省空间。在代码中用枚举类来映射。
  • phone:设为唯一约束,因为手机号通常是客户的唯一标识。这里有个坑:如果业务允许客户没有手机号,唯一约束可能会因为多个NULL值而失效(具体看数据库版本),需要根据实际情况处理。
  • create_time和update_time:这两个时间戳字段强烈建议加上。它们对于数据追溯、审计和分析非常有价值。ON UPDATE CURRENT_TIMESTAMP可以让记录在更新时自动刷新update_time。
  • utf8mb4字符集:支持存储Emoji等特殊字符,避免出现乱码问题。
  • 索引:在phone和name上建索引。phone是唯一索引,保证唯一性并加速基于手机号的查询。name是普通索引,加速按姓名模糊搜索(LIKE ‘%张%’)。但要注意,索引不是越多越好,会影响插入和更新速度。

3. 开发环境搭建与项目初始化

3.1 工具选型与配置

工欲善其事,必先利其器。一套顺手的开发环境能极大提升效率。

  1. JDK:选择JDK 8或JDK 11。JDK 8依然是目前企业中使用最广泛的LTS版本,兼容性最好。建议从Oracle官网或AdoptOpenJDK下载。
  2. IDE:IntelliJ IDEA Ultimate(学生可以免费申请许可)是首选。它对Java和Spring系列框架的支持是最好的,智能提示、代码生成、调试功能都非常强大。Eclipse也可以,但效率和体验上IDEA更优。
  3. 项目管理与构建工具:Maven。它帮你管理项目依赖(Jar包),你只需要在pom.xml文件中声明需要什么框架、什么版本,Maven会自动从中央仓库下载,并解决包之间的依赖关系。再也不用手动下载一堆Jar包了。
  4. 数据库:MySQL 5.7或8.0。安装时记住root密码。同时,需要一个图形化的数据库管理工具,Navicat或DBeaver(免费)都可以,方便你执行SQL脚本、查看数据。
  5. 应用服务器:本地开发使用Tomcat 8.5或9。IDEA可以很方便地集成Tomcat,进行热部署调试。

实操心得:在IDEA中配置Maven时,建议使用自己下载的Maven,并修改其conf/settings.xml文件,配置阿里云的镜像仓库,这样下载依赖包的速度会飞起。同时,将本地仓库路径改到非系统盘,避免C盘爆满。

3.2 创建Maven Web项目与SSM整合

这是项目的基础骨架搭建,步骤虽多,但每一步都有其意义。

  1. 创建项目:在IDEA中,选择新建Maven项目,选择maven-archetype-webapp原型。这会生成一个标准的Web项目结构。
  2. 完善目录结构:Maven原型生成的结构不完整,需要手动补全。这是标准的SSM项目目录:
    src/main/java com.yourcompany.crm.controller // 控制层 com.yourcompany.crm.service // 业务层接口 com.yourcompany.crm.service.impl // 业务层实现 com.yourcompany.crm.dao // 数据层接口(MyBatis Mapper接口) com.yourcompany.crm.entity // 实体类 com.yourcompany.crm.config // 配置类(可选,如果用Java Config) src/main/resources /spring // Spring配置文件 applicationContext.xml spring-mvc.xml /mybatis // MyBatis配置文件 mybatis-config.xml /mappers // MyBatis的SQL映射XML文件 CustomerMapper.xml jdbc.properties // 数据库连接属性文件 log4j2.xml // 日志配置文件 src/main/webapp /WEB-INF web.xml // Web部署描述符 /static /css /js /img /WEB-INF/jsp // JSP页面存放目录
  3. 编写pom.xml:这是项目的“食材清单”。你需要声明依赖,核心依赖包括:
    • Spring Core:spring-context,spring-beans,spring-core
    • Spring MVC:spring-webmvc
    • MyBatis:mybatis
    • MyBatis-Spring整合:mybatis-spring(关键!)
    • 数据库驱动:mysql-connector-java
    • 数据库连接池:推荐使用com.alibaba.druid,它功能强大,自带监控。
    • JSP相关:javax.servlet-api,javax.servlet.jsp-api,jstl
    • 日志:log4j-core,log4j-api(Log4j2) 或slf4j+logback
    • JSON处理:jackson-databind(用于Controller返回JSON数据给前端)
    • 测试:junit
  4. 配置web.xml:这是Web应用的入口。你需要在这里配置:
    • Spring的监听器:ContextLoaderListener,用于加载Spring的根容器(主要管理Service、Dao等Bean)。
    • Spring MVC的前端控制器:DispatcherServlet,用于拦截所有请求,加载Spring MVC的容器(主要管理Controller等Bean)。
    • 字符编码过滤器:解决中文乱码问题,必须配置。
    <!-- 字符编码过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Spring根容器配置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring MVC前端控制器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-mvc.xml</param-valu> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
  5. 配置Spring和MyBatis:这是整合的核心。
    • applicationContext.xml:配置数据源(Druid)、事务管理器、以及扫描Service和Dao层的Bean。
    • spring-mvc.xml:配置扫描Controller、视图解析器(告诉Spring MVC JSP文件在哪里)、静态资源处理、开启注解驱动。
    • mybatis-config.xml:配置MyBatis的全局设置,如驼峰命名映射、别名等。但很多配置可以合并到Spring配置中。
    • 关键整合步骤:在applicationContext.xml中配置SqlSessionFactoryBean,将MyBatis的SqlSessionFactory交给Spring管理,并注入数据源和Mapper XML文件的位置。再配置MapperScannerConfigurer,让它自动扫描Dao接口,并为其创建Spring Bean。

这个过程就像搭积木,每一步都是下一层的基础。配置完成后,启动Tomcat,如果控制台没有报错,并能看到Spring和MyBatis初始化的日志,那么恭喜你,环境搭建成功了。

4. 核心功能实现与代码详解

4.1 实体类与Mapper层实现

一切从定义数据开始。首先创建Customer实体类,属性对应数据库字段,并生成getter/setter方法。这里可以使用Lombok插件简化代码。

然后创建Dao层接口CustomerMapper.java。这里只定义方法,不写实现。

public interface CustomerMapper { int insert(Customer customer); int deleteById(Integer id); int update(Customer customer); Customer selectById(Integer id); List<Customer> selectAll(); List<Customer> selectByCondition(@Param("name") String name, @Param("phone") String phone); }

真正的SQL写在对应的XML文件CustomerMapper.xml中,放在resources/mappers目录下。

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.yourcompany.crm.dao.CustomerMapper"> <resultMap id="BaseResultMap" type="com.yourcompany.crm.entity.Customer"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="gender" property="gender"/> <result column="phone" property="phone"/> <!-- 其他字段映射 --> </resultMap> <insert id="insert" parameterType="Customer" useGeneratedKeys="true" keyProperty="id"> INSERT INTO t_customer (name, gender, phone, email, ...) VALUES (#{name}, #{gender}, #{phone}, #{email}, ...) </insert> <select id="selectByCondition" resultMap="BaseResultMap"> SELECT * FROM t_customer <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="phone != null and phone != ''"> AND phone = #{phone} </if> </where> </select> </mapper>

这里有个非常重要的技巧:<where>标签和<if>标签的使用。<where>标签会智能地处理WHERE关键字,如果里面所有的<if>条件都不成立,它不会生成WHERE,避免了WHERE后面直接跟AND的语法错误。<if>标签实现了动态SQL,根据传入的参数决定是否拼接某段SQL。这使得一个方法可以应对多种查询条件组合,非常灵活。

4.2 Service业务逻辑层封装

Service层是业务逻辑的核心。我们先定义接口CustomerService,再写实现类CustomerServiceImpl。这样做是为了面向接口编程,提高扩展性。

@Service // Spring注解,声明这是一个Service Bean public class CustomerServiceImpl implements CustomerService { @Autowired // Spring自动注入Mapper Bean private CustomerMapper customerMapper; @Override public boolean addCustomer(Customer customer) { // 业务校验:手机号格式、邮箱格式、非空检查等 if (customer.getPhone() == null || !isValidPhone(customer.getPhone())) { throw new IllegalArgumentException("手机号格式错误"); } // 检查手机号是否已存在 Customer exist = customerMapper.selectByPhone(customer.getPhone()); if (exist != null) { throw new RuntimeException("手机号已存在"); } // 设置创建时间等默认值 customer.setCreateTime(new Date()); customer.setUpdateTime(new Date()); // 调用Mapper插入数据 return customerMapper.insert(customer) > 0; } @Override @Transactional // 声明式事务注解,如果方法内发生异常,所有数据库操作回滚 public boolean updateCustomer(Customer customer) { // 更新前,先检查客户是否存在 Customer old = customerMapper.selectById(customer.getId()); if (old == null) { throw new RuntimeException("客户不存在"); } // 如果手机号有修改,需要检查新手机号是否已被他人使用 if (!old.getPhone().equals(customer.getPhone())) { Customer exist = customerMapper.selectByPhone(customer.getPhone()); if (exist != null) { throw new RuntimeException("新手机号已被其他客户使用"); } } customer.setUpdateTime(new Date()); return customerMapper.update(customer) > 0; } // ... 其他方法 }

事务管理:注意@Transactional注解。在更新操作中,我们可能先查询再更新,这应该是一个原子操作。加上这个注解后,Spring会为这个方法开启一个数据库事务,如果方法执行成功则提交,如果抛出运行时异常则回滚,保证了数据的一致性。

4.3 Controller层与前后端交互

Controller层接收HTTP请求,调用Service,然后返回结果。它像是一个服务员,接收顾客(前端)的点单(请求),交给后厨(Service)处理,再把做好的菜(响应)端回去。

@Controller // 声明这是一个Controller @RequestMapping("/customer") // 类级别的映射,所有方法路径前都会加上/customer public class CustomerController { @Autowired private CustomerService customerService; // 跳转到客户列表页 @GetMapping("/list") public String listPage() { return "customer/list"; // 对应 /WEB-INF/jsp/customer/list.jsp } // 获取客户列表数据(JSON格式,供前端表格异步加载) @GetMapping("/api/list") @ResponseBody // 表示返回的是JSON数据,而不是视图名 public Map<String, Object> getCustomerList( @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, @RequestParam(value = "name", required = false) String name) { // 调用Service进行分页查询 PageHelper.startPage(pageNum, pageSize); // 使用PageHelper物理分页 List<Customer> list = customerService.getCustomersByName(name); PageInfo<Customer> pageInfo = new PageInfo<>(list); Map<String, Object> result = new HashMap<>(); result.put("success", true); result.put("data", pageInfo.getList()); // 当前页数据 result.put("total", pageInfo.getTotal()); // 总记录数 return result; } // 新增客户 @PostMapping("/api/add") @ResponseBody public Map<String, Object> addCustomer(@RequestBody Customer customer) { // @RequestBody接收JSON格式的请求体 Map<String, Object> result = new HashMap<>(); try { customerService.addCustomer(customer); result.put("success", true); result.put("message", "添加成功"); } catch (Exception e) { result.put("success", false); result.put("message", "添加失败: " + e.getMessage()); } return result; } }

关键点解析:

  • @GetMapping,@PostMapping:这是Spring MVC的注解,分别映射HTTP的GET和POST请求。RESTful风格建议:获取数据用GET,新增用POST,更新用PUT,删除用DELETE。
  • @ResponseBody:这个注解至关重要。它告诉Spring MVC,方法的返回值不是视图名,而是要直接写入HTTP响应体的数据(通常转换为JSON)。配合@RestController注解(等于@Controller+@ResponseBody)使用更简洁。
  • @RequestBody:用于将前端发送的JSON字符串,自动绑定到后端的Java对象上。
  • 分页:我们使用了PageHelper这个国产开源分页插件。它通过拦截MyBatis的SQL,自动拼接分页语句(LIMIT),非常方便。PageInfo对象包含了分页的所有信息(当前页、总页数、数据列表等)。

4.4 前端页面与数据绑定

前端我们使用简单的JSP + Bootstrap + jQuery。JSP负责页面骨架,Bootstrap负责样式,jQuery负责发起Ajax请求与后端交互。

在list.jsp中,我们会有一个表格来展示客户列表。

<table id="customerTable" class="table table-hover"> <thead><tr><th>ID</th><th>姓名</th><th>手机号</th><th>操作</th></tr></thead> <tbody><!-- 数据由JS动态加载 --></tbody> </table> <nav id="pagination"><!-- 分页组件 --></nav>

使用jQuery的Ajax来加载数据:

function loadCustomerData(pageNum, name) { $.ajax({ url: '${pageContext.request.contextPath}/customer/api/list', type: 'GET', data: {pageNum: pageNum, pageSize: 10, name: name}, dataType: 'json', success: function(result) { if (result.success) { // 1. 清空表格 $('#customerTable tbody').empty(); // 2. 遍历数据,拼接HTML行 $.each(result.data, function(index, customer) { var row = '<tr>' + '<td>' + customer.id + '</td>' + '<td>' + customer.name + '</td>' + '<td>' + customer.phone + '</td>' + '<td>' + '<button class="btn btn-sm btn-primary" onclick="editCustomer(' + customer.id + ')">编辑</button> ' + '<button class="btn btn-sm btn-danger" onclick="deleteCustomer(' + customer.id + ')">删除</button>' + '</td>' + '</tr>'; $('#customerTable tbody').append(row); }); // 3. 渲染分页组件 renderPagination(result.total, pageNum); } } }); }

前后端分离的雏形:虽然我们用了JSP,但数据是通过Ajax异步获取的JSON,页面渲染在浏览器端完成。这已经是前后端分离的一种简单形式。这样做的好处是页面无需刷新,用户体验好,后端Controller只负责提供数据接口,职责清晰。

5. 项目部署、测试与常见问题排查

5.1 项目打包与部署

开发完成后,需要将项目部署到服务器或交给老师验收。Maven的打包命令是mvn clean package。对于Web项目,会生成一个项目名.war文件。

  1. 本地Tomcat部署:将生成的.war文件复制到Tomcat的webapps目录下,启动Tomcat,它会自动解压并部署。访问http://localhost:8080/你的项目名/即可。
  2. 外置Tomcat部署(IDEA):在IDEA的Run/Debug Configuration中配置Tomcat,指定Deployment为Artifact,选择你项目生成的war包。这样可以在IDEA中直接启动和调试。
  3. 打可执行Jar包(Spring Boot方式):如果你的项目后期想改为Spring Boot,可以使用spring-boot-maven-plugin打包成一个包含内嵌Tomcat的、可直接通过java -jar运行的Jar包,部署极其方便。但对于纯SSM项目,通常还是打War包。

5.2 功能测试要点

测试不能只靠眼睛看,要有计划。

  1. 单元测试:使用JUnit对Service层的方法进行测试。确保每个业务逻辑(如新增时的校验、更新时的检查)都正确。可以使用@RunWith(SpringJUnit4ClassRunner.class)和@ContextConfiguration加载Spring配置,然后@Autowired注入Service进行测试。
  2. 接口测试:使用Postman或浏览器直接访问Controller的API接口,检查返回的JSON数据是否正确。重点测试边界情况:传入空值、超长字符串、不存在的ID等。
  3. 界面集成测试:在浏览器中手动操作,覆盖所有功能点:增、删、改、查、分页、条件搜索。检查页面交互是否流畅,错误提示是否友好。
  4. 数据库验证:在Navicat里直接查看数据表,确认操作后的数据状态是否符合预期,特别是时间戳字段。

5.3 常见问题与解决方案实录

以下是我在开发和教学过程中,学生遇到最高频的几个问题:

问题现象可能原因排查步骤与解决方案
启动Tomcat时报ClassNotFoundException或NoClassDefFoundError1. 依赖的Jar包没有正确引入。
2. Jar包版本冲突。
3. Tomcat提供的Servlet-api与项目中的冲突。
1. 检查pom.xml依赖,运行mvn clean compile看是否报错。
2. 在IDEA中打开Project Structure -> Modules -> Dependencies,查看是否有红色波浪线。
3. 将servlet-api的依赖范围设为provided,表示该包由Tomcat容器提供。
页面中文乱码1. 数据库连接字符集不是UTF-8。
2. 请求/响应编码未设置。
3. JSP页面编码未设置。
1. 检查数据库、表、字段的字符集是否为utf8mb4。
2. 确认web.xml中配置了字符编码过滤器,且forceEncoding为true。
3. 在JSP页面头部添加<%@ page contentType="text/html;charset=UTF-8" language="java" %>。
4. 在Spring MVC配置中设置<mvc:annotation-driven>并配置消息转换器。
404错误(页面找不到)1. 请求路径错误。
2.DispatcherServlet的url-pattern配置问题。
3. Controller方法未被扫描到。
1. 检查浏览器地址栏URL是否与@RequestMapping的值匹配。
2. 检查web.xml中DispatcherServlet的<url-pattern>,通常为/。
3. 检查spring-mvc.xml中组件扫描的包路径是否包含了Controller类。
500错误(服务器内部错误)1. 空指针异常(NPE)。
2. SQL异常。
3. 类型转换错误。
1.看控制台日志!Tomcat的catalina.out或IDEA的控制台会打印详细的异常堆栈信息,这是定位问题的关键。
2. 检查Service或Controller中自动注入(@Autowired)的对象是否为null。
3. 检查MyBatis的SQL映射文件,参数#{}中的属性名是否与实体类一致。
@Autowired注入失败,对象为null1. 该类没有被Spring管理(缺少@Component,@Service等注解)。
2. 包路径不在Spring的扫描范围内。
3. 存在多个同类型的Bean。
1. 确保被注入的类(如CustomerServiceImpl)上有@Service注解。
2. 检查applicationContext.xml中的<context:component-scan>的base-package是否包含了该类的包。
3. 使用@Qualifier注解指定Bean的名称。
MyBatis查询结果为空或字段映射失败1. SQL语句有误。
2. 数据库连接错误,连到了别的库或表。
3. 实体类字段名与数据库列名不一致,未开启驼峰映射。
1. 将MyBatis的日志级别设为DEBUG,在控制台查看实际执行的SQL。
2. 检查jdbc.properties中的数据库名、表名。
3. 在MyBatis配置中设置<setting name="mapUnderscoreToCamelCase" value="true"/>,开启自动驼峰命名映射(user_name->userName)。
事务@Transactional不生效1. 方法不是public的。
2. 异常类型不是运行时异常(RuntimeException)。
3. 在同一个类内部方法调用,走了代理对象。
1. 确保事务方法为public。
2. 默认只回滚运行时异常和Error。可配置@Transactional(rollbackFor = Exception.class)回滚所有异常。
3. 避免在同一个Service类中,一个非事务方法调用另一个事务方法。

一个典型的排错流程:遇到问题 -> 1. 看浏览器控制台(F12)的Network和Console标签,看请求是否发出、状态码、响应内容。 -> 2. 看服务器(IDEA)控制台日志,找异常堆栈。 -> 3. 根据异常信息,定位到具体的代码行。 -> 4. 使用Debug模式,打断点,逐步执行,观察变量状态。

5.4 项目扩展与优化建议

如果你想让你的毕设脱颖而出,可以尝试以下扩展方向,这会在答辩时给你加分:

  1. 加入权限控制:使用Spring Security或Shiro框架,实现简单的登录、角色管理(如管理员和普通用户)、权限拦截。让系统更完整。
  2. 实现文件上传:允许为客户上传头像或合同附件。Spring MVC对文件上传有很好的支持,结合OSS(对象存储)会更专业。
  3. 添加数据导出功能:使用Apache POI或EasyExcel库,将客户列表导出为Excel文件。
  4. 引入缓存:对于不常变动的数据(如客户类型字典),可以使用Redis进行缓存,减轻数据库压力,提升查询速度。
  5. 前端框架升级:将JSP替换为更现代化的前端框架,如Vue.js或React,实现真正的前后端分离。后端Controller只提供RESTful API。
  6. 改为Spring Boot:尝试将项目迁移到Spring Boot,体验一下“约定大于配置”的便捷,对比SSM和Spring Boot的异同,这本身就是一个很好的学习点。

做这个项目,最重要的不是把代码敲完,而是在这个过程中,理解每一个配置的意义,每一行代码的作用,遇到问题并解决它。当你看到浏览器上成功显示出从数据库查出来的客户列表时,那种成就感就是学习编程最大的动力。这个项目做完,Java Web开发的大门,你就已经推开一半了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询