SpringMVC 实战:整合 MyBatis 实现完整 CRUD
引言
前面两篇我们掌握了 SpringMVC 的基础和进阶技术,但实际项目中,SpringMVC 很少单独使用,通常需要与持久层框架(如 MyBatis)整合,实现 “接收请求→业务处理→操作数据库→返回结果” 的完整流程。本文将带你从零搭建 “SpringMVC + MyBatis” 的项目,实现用户管理的 CRUD 功能,覆盖实际开发中的核心配置和最佳实践。
一、整合思路与技术栈
1. 技术栈选型
技术 | 版本 | 作用 |
---|---|---|
SpringMVC | 5.3.28 | 控制层,接收请求、返回视图 |
Spring | 5.3.28 | 核心容器,管理 Service、DAOBean |
MyBatis | 3.5.13 | 持久层,操作数据库 |
MyBatis-Spring | 2.1.2 | 整合 Spring 与 MyBatis |
MySQL | 8.0 | 数据库 |
Druid | 1.2.16 | 数据库连接池(高效、安全) |
JSP + JSTL | JSP 2.2 | 前端视图 |
Maven | 3.8 | 项目构建与依赖管理 |
Tomcat | 9.0 | 服务器 |
2. 整合核心思路
采用 “分层架构”,各层职责清晰,通过 Spring 的 IOC 容器整合各层 Bean:
- Controller 层(SpringMVC):接收请求,调用 Service 层;
- Service 层(Spring):处理业务逻辑,调用 DAO 层,管理事务;
- DAO 层(MyBatis):定义数据访问接口,MyBatis 实现 SQL 操作;
- Spring 容器:
- 父容器(
spring.xml
):管理 Service、DAO、数据源等非 Web 组件; - 子容器(
spring-mvc.xml
):管理 Controller 组件,子容器可访问父容器的 Bean,反之不行。
- 父容器(
二、环境搭建:从零开始配置
1. 步骤 1:创建 Maven Web 项目
同博客一的 “步骤 1”,项目名改为springmvc-mybatis-crud
。
2. 步骤 2:导入整合依赖(pom.xml)
核心依赖包括 Spring、SpringMVC、MyBatis、MyBatis-Spring、MySQL 驱动、Druid 连接池:
xml
<dependencies><!-- 1. Spring核心 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.28</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.28</version></dependency><!-- 2. SpringMVC --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.28</version></dependency><!-- 3. MyBatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.13</version></dependency><!-- 4. MyBatis-Spring整合 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.1.2</version></dependency><!-- 5. MySQL驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!-- 6. Druid连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.16</version></dependency><!-- 7. Servlet & JSP --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet.jsp.jstl</groupId><artifactId>jstl-api</artifactId><version>1.2</version></dependency><dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1.1.2</version></dependency>
</dependencies><!-- 配置Maven编译插件,指定JDK版本 -->
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin></plugins>
</build>
3. 步骤 3:配置 web.xml(整合核心)
web.xml
需配置 3 个关键内容:Spring 父容器初始化、SpringMVC 前端控制器、编码过滤器:
xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="4.0"><!-- 1. 配置编码过滤器(解决中文乱码) --><filter><filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- 2. 初始化Spring父容器(加载非Web组件:Service、DAO、数据源) --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- 3. 配置SpringMVC前端控制器(加载Web组件:Controller) --><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
4. 步骤 4:配置 Spring 父容器(spring.xml)
spring.xml
主要配置:数据源、SqlSessionFactory、DAO 扫描、Service 扫描、事务管理。
(1)配置数据源(Druid)
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 1. 加载数据库配置文件(可选,推荐将配置放在properties文件中) --><context:property-placeholder location="classpath:db.properties"/><!-- 2. 配置Druid数据源 --><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${db.driver}"/><property name="url" value="${db.url}"/><property name="username" value="${db.username}"/><property name="password" value="${db.password}"/><!-- 连接池配置(可选) --><property name="initialSize" value="5"/><property name="maxActive" value="20"/></bean>
(2)配置 SqlSessionFactory(MyBatis 核心)
xml
<!-- 3. 配置SqlSessionFactory(MyBatis-Spring提供) --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/> <!-- 关联数据源 --><property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- MyBatis全局配置 --><property name="mapperLocations" value="classpath:mapper/*.xml"/> <!-- Mapper XML文件路径 --><property name="typeAliasesPackage" value="com.example.pojo"/> <!-- POJO别名扫描(简化XML配置) --></bean>
(3)配置 DAO 接口扫描(MyBatis-Spring)
xml
<!-- 4. 扫描DAO接口,生成代理对象(无需手动写实现类) --><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.example.dao"/> <!-- DAO接口所在包 --><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/></bean>
(4)配置 Service 扫描与事务管理
xml
<!-- 5. 扫描Service组件 --><context:component-scan base-package="com.example.service"/><!-- 6. 配置事务管理器(Spring JDBC提供) --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!-- 7. 开启事务注解驱动(@Transactional) --><tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
5. 步骤 5:配置 MyBatis 全局文件(mybatis-config.xml)
MyBatis 全局配置主要用于设置 “日志、缓存” 等,简化版配置如下:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 配置日志(可选,用于打印SQL) --><settings><setting name="logImpl" value="STDOUT_LOGGING"/></settings>
</configuration>
6. 步骤 6:配置 SpringMVC 子容器(spring-mvc.xml)
spring-mvc.xml
配置Controller 扫描、注解驱动、视图解析器、静态资源处理:
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 1. 扫描Controller组件(只扫描Controller,避免与Spring父容器重复) --><context:component-scan base-package="com.example.controller"><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><!-- 2. 开启SpringMVC注解驱动(参数绑定、JSON解析等) --><mvc:annotation-driven/><!-- 3. 配置静态资源处理(放行CSS、JS、图片等,避免被DispatcherServlet拦截) --><mvc:default-servlet-handler/><!-- 4. 配置JSP视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/"/><property name="suffix" value=".jsp"/><property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/></bean>
</beans>
7. 步骤 7:创建数据库表(MySQL)
创建user
表,用于存储用户信息:
sql
CREATE DATABASE IF NOT EXISTS springmvc_mybatis;
USE springmvc_mybatis;CREATE TABLE IF NOT EXISTS `user` (`id` INT PRIMARY KEY AUTO_INCREMENT,`username` VARCHAR(50) NOT NULL,`age` INT,`email` VARCHAR(100)
);-- 插入测试数据
INSERT INTO `user` (`username`, `age`, `email`) VALUES
('张三', 20, 'zhangsan@xxx.com'),
('李四', 25, 'lisi@xxx.com');
8. 步骤 8:创建 db.properties(数据库配置)
在resources
目录下新建db.properties
,存储数据库连接信息(避免硬编码):
properties
# MySQL 8.0+驱动类
db.driver=com.mysql.cj.jdbc.Driver
# URL(useSSL=false:禁用SSL;serverTimezone=UTC:设置时区)
db.url=jdbc:mysql://localhost:3306/springmvc_mybatis?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
# 数据库用户名(替换为你的)
db.username=root
# 数据库密码(替换为你的)
db.password=123456
三、实现 CRUD 功能:分层开发
1. 项目结构梳理
先明确各层的包结构(在src/main/java
下):
plaintext
com.example
├── controller # 控制层(UserController)
├── service # 业务层(UserService接口 + UserServiceImpl实现)
├── dao # 数据访问层(UserDao接口)
└── pojo # 实体类(User)
在resources
目录下:
plaintext
resources
├── db.properties # 数据库配置
├── spring.xml # Spring父容器配置
├── spring-mvc.xml # SpringMVC子容器配置
├── mybatis-config.xml # MyBatis全局配置
└── mapper # MyBatis Mapper XML(UserMapper.xml)
在WEB-INF
下:
plaintext
WEB-INF
├── views # JSP视图
│ ├── user
│ │ ├── list.jsp # 用户列表页
│ │ ├── add.jsp # 添加用户页
│ │ └── edit.jsp # 编辑用户页
│ └── success.jsp # 操作成功页
└── web.xml # Web配置
2. 步骤 1:编写 POJO 实体类(User.java)
java
运行
package com.example.pojo;public class User {private Integer id;private String username;private Integer age;private String email;// 默认无参构造器public User() {}// 带参构造器(可选)public User(String username, Integer age, String email) {this.username = username;this.age = age;this.email = email;}// Getter和Setter方法(必须)public Integer getId() { return id; }public void setId(Integer id) { this.id = id; }public String getUsername() { return username; }public void setUsername(String username) { this.username = username; }public Integer getAge() { return age; }public void setAge(Integer age) { this.age = age; }public String getEmail() { return email; }public void setEmail(String email) { this.email = email; }// toString方法(方便打印)@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", age=" + age +", email='" + email + '\'' +'}';}
}
3. 步骤 2:编写 DAO 层(UserDao 接口 + UserMapper.xml)
(1)UserDao 接口(无需实现类,MyBatis 动态代理)
java
运行
package com.example.dao;import com.example.pojo.User;
import org.apache.ibatis.annotations.Param;import java.util.List;public interface UserDao {// 1. 查询所有用户List<User> findAll();// 2. 根据ID查询用户User findById(@Param("id") Integer id); // @Param:指定SQL中的参数名// 3. 添加用户void addUser(User user);// 4. 更新用户void updateUser(User user);// 5. 删除用户void deleteUser(@Param("id") Integer id);
}
(2)UserMapper.xml(MyBatis SQL 实现)
在resources/mapper
目录下新建UserMapper.xml
:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:对应DAO接口全路径 -->
<mapper namespace="com.example.dao.UserDao"><!-- 1. 查询所有用户 --><select id="findAll" resultType="User"> <!-- resultType:POJO别名(spring.xml中配置了别名扫描) -->SELECT id, username, age, email FROM user</select><!-- 2. 根据ID查询用户 --><select id="findById" parameterType="int" resultType="User">SELECT id, username, age, email FROM user WHERE id = #{id}</select><!-- 3. 添加用户 --><insert id="addUser" parameterType="User">INSERT INTO user (username, age, email) VALUES (#{username}, #{age}, #{email})</insert><!-- 4. 更新用户 --><update id="updateUser" parameterType="User">UPDATE user SET username = #{username}, age = #{age}, email = #{email} WHERE id = #{id}</update><!-- 5. 删除用户 --><delete id="deleteUser" parameterType="int">DELETE FROM user WHERE id = #{id}</delete>
</mapper>
4. 步骤 3:编写 Service 层(UserService 接口 + UserServiceImpl 实现)
(1)UserService 接口
java
运行
package com.example.service;import com.example.pojo.User;import java.util.List;public interface UserService {// 1. 查询所有用户List<User> findAllUsers();// 2. 根据ID查询用户User findUserById(Integer id);// 3. 添加用户void addUser(User user);// 4. 更新用户void updateUser(User user);// 5. 删除用户void deleteUser(Integer id);
}
(2)UserServiceImpl 实现类(注入 DAO,处理事务)
java
运行
package com.example.service.impl;import com.example.dao.UserDao;
import com.example.pojo.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service // 标记为Service组件,交给Spring管理
@Transactional // 开启事务(所有方法都受事务管理)
public class UserServiceImpl implements UserService {// 注入UserDao(MyBatis动态代理生成的实现类)@Autowiredprivate UserDao userDao;@Overridepublic List<User> findAllUsers() {return userDao.findAll();}@Overridepublic User findUserById(Integer id) {return userDao.findById(id);}@Overridepublic void addUser(User user) {userDao.addUser(user);}@Overridepublic void updateUser(User user) {userDao.updateUser(user);}@Overridepublic void deleteUser(Integer id) {userDao.deleteUser(id);}
}
5. 步骤 4:编写 Controller 层(UserController)
处理用户相关的 5 个请求:查询所有、查询单个、添加、编辑、删除。
java
运行
package com.example.controller;import com.example.pojo.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller
@RequestMapping("/user") // 类级别的请求映射,所有方法路径都以/user开头
public class UserController {// 注入UserService(Spring父容器的Bean,子容器可访问)@Autowiredprivate UserService userService;// 1. 查询所有用户(跳转到列表页)@RequestMapping("/list")public String findAll(Model model) {List<User> userList = userService.findAllUsers();model.addAttribute("userList", userList); // 存入Model,供JSP使用return "user/list"; // 跳转到user/list.jsp}// 2. 跳转到添加用户页面@RequestMapping("/toAdd")public String toAdd() {return "user/add"; // 跳转到user/add.jsp}// 3. 执行添加用户操作@RequestMapping("/add")public String addUser(User user) {userService.addUser(user);return "redirect:/user/list"; // 重定向到用户列表页(避免表单重复提交)}// 4. 跳转到编辑用户页面(携带用户ID)@RequestMapping("/toEdit")public String toEdit(Integer id, Model model) {User user = userService.findUserById(id);model.addAttribute("user", user); // 传入编辑页的用户数据return "user/edit"; // 跳转到user/edit.jsp}// 5. 执行更新用户操作@RequestMapping("/update")public String updateUser(User user) {userService.updateUser(user);return "redirect:/user/list"; // 重定向到列表页}// 6. 执行删除用户操作@RequestMapping("/delete")public String deleteUser(Integer id) {userService.deleteUser(id);return "redirect:/user/list"; // 重定向到列表页}
}
6. 步骤 5:编写前端 JSP 视图
(1)用户列表页(list.jsp)
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!-- 导入JSTL标签 -->
<html>
<head><title>用户列表</title><style>table { border-collapse: collapse; width: 80%; margin: 20px auto; }th, td { border: 1px solid #ccc; padding: 8px; text-align: center; }th { background-color: #f5f5f5; }.add-btn { margin-left: 10%; text-decoration: none; color: blue; }</style>
</head>
<body><!-- 添加用户按钮 --><a href="${pageContext.request.contextPath}/user/toAdd" class="add-btn">添加用户</a><!-- 用户列表表格 --><table><tr><th>ID</th><th>用户名</th><th>年龄</th><th>邮箱</th><th>操作</th></tr><!-- JSTL循环遍历userList --><c:forEach items="${userList}" var="user"><tr><td>${user.id}</td><td>${user.username}</td><td>${user.age}</td><td>${user.email}</td><td><!-- 编辑按钮:携带用户ID --><a href="${pageContext.request.contextPath}/user/toEdit?id=${user.id}">编辑</a><!-- 删除按钮:携带用户ID,确认提示 --><a href="${pageContext.request.contextPath}/user/delete?id=${user.id}" onclick="return confirm('确定要删除吗?')">删除</a></td></tr></c:forEach></table>
</body>
</html>
(2)添加用户页(add.jsp)
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>添加用户</title><style>form { width: 40%; margin: 20px auto; }.form-group { margin: 10px 0; }label { display: inline-block; width: 80px; }input { padding: 5px; width: 200px; }.submit-btn { margin-left: 83px; padding: 6px 20px; }</style>
</head>
<body><form action="${pageContext.request.contextPath}/user/add" method="post"><div class="form-group"><label>用户名:</label><input type="text" name="username" required> <!-- required:前端校验非空 --></div><div class="form-group"><label>年龄:</label><input type="number" name="age" required></div><div class="form-group"><label>邮箱:</label><input type="email" name="email" required></div><div class="form-group"><button type="submit" class="submit-btn">添加</button></div></form>
</body>
</html>
(3)编辑用户页(edit.jsp)
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>编辑用户</title><style>form { width: 40%; margin: 20px auto; }.form-group { margin: 10px 0; }label { display: inline-block; width: 80px; }input { padding: 5px; width: 200px; }.submit-btn { margin-left: 83px; padding: 6px 20px; }</style>
</head>
<body><!-- 编辑表单:提交到/update,携带用户ID(隐藏域) --><form action="${pageContext.request.contextPath}/user/update" method="post"><input type="hidden" name="id" value="${user.id}"> <!-- 隐藏域:传递用户ID --><div class="form-group"><label>用户名:</label><input type="text" name="username" value="${user.username}" required></div><div class="form-group"><label>年龄:</label><input type="number" name="age" value="${user.age}" required></div><div class="form-group"><label>邮箱:</label><input type="email" name="email" value="${user.email}" required></div><div class="form-group"><button type="submit" class="submit-btn">更新</button></div></form>
</body>
</html>
四、测试与运行
1. 部署项目
- 配置 Tomcat(同博客一),将
springmvc-mybatis-crud
部署到 Tomcat; - 启动 Tomcat,确保数据库服务已开启。
2. 测试 CRUD 功能
- 访问用户列表页:
http://localhost:8080/springmvc-mybatis-crud/user/list
,可看到初始的 2 条测试数据; - 测试 “添加用户”:点击 “添加用户”,填写表单并提交,列表页会显示新添加的用户;
- 测试 “编辑用户”:点击某条数据的 “编辑”,修改信息后提交,列表页数据更新;
- 测试 “删除用户”:点击 “删除”,确认后数据从列表中移除。
五、实战小结与优化建议
-
核心收获:
- 掌握 “SpringMVC + MyBatis” 的分层整合思路,理解父子容器的关系;
- 学会用 MyBatis-Spring 的
MapperScannerConfigurer
简化 DAO 层开发; - 理解 “重定向(redirect)” 避免表单重复提交的原理。
-
优化建议(实际项目中常用):
- 分页查询:用户数据多时,用 MyBatis 分页插件(如 PageHelper)实现分页;
- 参数校验:用 Hibernate Validator(如
@NotBlank
、@Min
)替代前端校验,确保数据合法性; - 异常处理:自定义全局异常处理器(实现
HandlerExceptionResolver
),统一处理异常; - 前后端分离:将 JSP 替换为 Vue/React,Controller 用
@ResponseBody
返回 JSON,提升用户体验。
-
源码获取:本文完整源码已上传至 GitHub(地址:github.com/xxx/springmvc-mybatis-crud),可直接下载运行。
覆盖了 SpringMVC 的核心知识点和实际应用,你可以根据学习进度逐步阅读和实践。如果在搭建项目或理解知识点时遇到问题,欢迎在评论区交流!