SSM项目实战教程 - Spring+SpringMVC+MyBatis完整开发指南
大约 21 分钟
SSM项目实战教程 - Spring+SpringMVC+MyBatis完整开发指南
目录
- SSM框架简介
- 环境搭建
- 项目结构设计
- 数据库设计
- Maven依赖配置
- Spring配置文件
- SpringMVC配置
- MyBatis配置
- 实体类开发
- Mapper层开发
- Service层开发
- Controller层开发
- 前端页面开发
- 项目测试
- 项目部署
- 常见问题解决
- 总结与进阶
1. SSM框架简介
SSM是三个Java框架的整合:
Spring:IoC容器和AOP框架,负责依赖注入和面向切面编程
SpringMVC:MVC框架,负责Web层的请求处理和视图解析
MyBatis:持久层框架,负责数据库操作和SQL映射
✅ 分层清晰:MVC架构,职责分明
✅ 松耦合:通过Spring IoC实现组件解耦
✅ 易维护:代码结构清晰,易于维护
✅ 灵活性强:MyBatis支持自定义SQL,灵活高效
✅ 生态完善:Spring生态丰富,集成方便
┌─────────────────────────────────────────┐
│ 前端页面(JSP/HTML) │
└─────────────────┬───────────────────────┘
│ HTTP请求
┌─────────────────▼───────────────────────┐
│ SpringMVC(Controller层) │
│ - 接收请求 │
│ - 参数绑定 │
│ - 调用Service │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ Spring(Service层) │
│ - 业务逻辑处理 │
│ - 事务管理 │
│ - 调用Mapper │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ MyBatis(Mapper层) │
│ - SQL映射 │
│ - 数据库操作 │
└─────────────────┬───────────────────────┘
│
┌─────────────────▼───────────────────────┐
│ MySQL数据库 │
└─────────────────────────────────────────┘本教程将通过开发一个用户管理系统来演示SSM框架的完整整合过程,包含:
- 用户的增删改查(CRUD)
- 分页查询
- 条件查询
- 前后端交互
- 数据验证
2. 环境搭建
- JDK:JDK 8或更高版本
- IDE:IntelliJ IDEA或Eclipse
- Maven:3.6+
- MySQL:5.7+或8.0+
- Tomcat:8.5+或9.0+
- 浏览器:Chrome、Firefox等
JDK安装验证
java -version
javac -versionMaven安装验证
mvn -versionMySQL安装和配置
- 安装MySQL数据库
- 创建数据库和用户:
-- 创建数据库
CREATE DATABASE ssm_demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建用户(可选)
CREATE USER 'ssm_user'@'localhost' IDENTIFIED BY 'ssm_password';
GRANT ALL PRIVILEGES ON ssm_demo.* TO 'ssm_user'@'localhost';
FLUSH PRIVILEGES;IntelliJ IDEA配置
- 配置Maven:File → Settings → Build → Build Tools → Maven
- 配置JDK:File → Project Structure → Project SDK
- 配置Tomcat:Run → Edit Configurations → 添加Tomcat Server
3. 项目结构设计
ssm-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── ssm/
│ │ │ ├── controller/ # 控制层
│ │ │ ├── service/ # 服务层
│ │ │ │ └── impl/ # 服务实现层
│ │ │ ├── mapper/ # 数据访问层
│ │ │ ├── entity/ # 实体类
│ │ │ ├── dto/ # 数据传输对象
│ │ │ ├── util/ # 工具类
│ │ │ └── config/ # 配置类(可选)
│ │ ├── resources/
│ │ │ ├── spring/ # Spring配置
│ │ │ │ ├── spring-dao.xml
│ │ │ │ ├── spring-service.xml
│ │ │ │ └── spring-mvc.xml
│ │ │ ├── mybatis/ # MyBatis配置
│ │ │ │ └── mybatis-config.xml
│ │ │ └── mapper/ # Mapper XML文件
│ │ │ └── UserMapper.xml
│ │ └── webapp/
│ │ ├── WEB-INF/
│ │ │ ├── web.xml # Web配置
│ │ │ └── views/ # JSP视图
│ │ │ ├── user/
│ │ │ │ ├── list.jsp
│ │ │ │ ├── add.jsp
│ │ │ │ └── edit.jsp
│ │ │ └── index.jsp
│ │ ├── static/ # 静态资源
│ │ │ ├── css/
│ │ │ ├── js/
│ │ │ └── images/
│ │ └── index.jsp
│ └── test/
│ └── java/ # 测试代码
└── pom.xml # Maven配置- controller:处理HTTP请求,调用Service层
- service:业务逻辑处理,调用Mapper层
- mapper:数据访问接口,对应MyBatis XML
- entity:实体类,对应数据库表
- dto:数据传输对象,用于前后端数据交互
- util:工具类,如分页工具、日期工具等
4. 数据库设计
USE ssm_demo;
-- 用户表
CREATE TABLE IF NOT EXISTS `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`email` VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
`phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号',
`age` INT(3) DEFAULT NULL COMMENT '年龄',
`gender` TINYINT(1) DEFAULT 0 COMMENT '性别:0-未知,1-男,2-女',
`status` TINYINT(1) DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 插入测试数据
INSERT INTO `user` (`username`, `password`, `email`, `phone`, `age`, `gender`, `status`) VALUES
('admin', '123456', 'admin@example.com', '13800138000', 25, 1, 1),
('zhangsan', '123456', 'zhangsan@example.com', '13800138001', 28, 1, 1),
('lisi', '123456', 'lisi@example.com', '13800138002', 26, 2, 1),
('wangwu', '123456', 'wangwu@example.com', '13800138003', 30, 1, 0);5. Maven依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>ssm-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SSM Demo</name>
<description>SSM框架整合实战项目</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!-- 版本管理 -->
<spring.version>5.3.23</spring.version>
<mybatis.version>3.5.13</mybatis.version>
<mybatis-spring.version>2.1.0</mybatis-spring.version>
<mysql.version>8.0.33</mysql.version>
<druid.version>1.2.16</druid.version>
<jackson.version>2.15.2</jackson.version>
<jstl.version>1.2</jstl.version>
<servlet-api.version>4.0.1</servlet-api.version>
<jsp-api.version>2.3.3</jsp-api.version>
<junit.version>4.13.2</junit.version>
</properties>
<dependencies>
<!-- Spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring事务 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- MyBatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- MyBatis-Spring整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- Druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- Jackson JSON处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- JSTL标签库 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<!-- JSP API -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>${jsp-api.version}</version>
<scope>provided</scope>
</dependency>
<!-- 日志框架 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
</dependency>
<!-- 参数验证 -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.3</version>
</dependency>
<!-- JUnit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring测试 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>ssm-demo</finalName>
<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>
<!-- Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/ssm-demo</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
</project>6. Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 加载数据库配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 连接池配置 -->
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxWait" value="${jdbc.maxWait}"/>
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<property name="typeAliasesPackage" value="com.example.ssm.entity"/>
</bean>
<!-- 配置Mapper扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.ssm.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描Service包 -->
<context:component-scan base-package="com.example.ssm.service"/>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 扫描Controller包 -->
<context:component-scan base-package="com.example.ssm.controller"/>
<!-- 开启MVC注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 静态资源处理 -->
<mvc:resources mapping="/static/**" location="/static/"/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
</beans># 数据库驱动
jdbc.driver=com.mysql.cj.jdbc.Driver
# 数据库连接URL
jdbc.url=jdbc:mysql://localhost:3306/ssm_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8&useUnicode=true
# 数据库用户名
jdbc.username=root
# 数据库密码
jdbc.password=root
# 连接池配置
jdbc.initialSize=5
jdbc.maxActive=20
jdbc.minIdle=5
jdbc.maxWait=60000<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 开启驼峰命名转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 日志实现 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.example.ssm.entity"/>
</typeAliases>
</configuration>7. SpringMVC配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>SSM Demo</display-name>
<!-- 字符编码过滤器 -->
<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>
<!-- Spring容器配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring/spring-dao.xml,
classpath:spring/spring-service.xml
</param-value>
</context-param>
<!-- Spring监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- SpringMVC前端控制器 -->
<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-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 默认首页 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>8. MyBatis配置
<?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.example.ssm.mapper.UserMapper">
<!-- 结果映射 -->
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="phone" column="phone"/>
<result property="age" column="age"/>
<result property="gender" column="gender"/>
<result property="status" column="status"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- 根据ID查询 -->
<select id="selectById" parameterType="int" resultMap="userResultMap">
SELECT id, username, password, email, phone, age, gender, status,
create_time, update_time
FROM user
WHERE id = #{id}
</select>
<!-- 查询所有 -->
<select id="selectAll" resultMap="userResultMap">
SELECT id, username, password, email, phone, age, gender, status,
create_time, update_time
FROM user
ORDER BY create_time DESC
</select>
<!-- 条件查询 -->
<select id="selectByCondition" parameterType="map" resultMap="userResultMap">
SELECT id, username, password, email, phone, age, gender, status,
create_time, update_time
FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email LIKE CONCAT('%', #{email}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
ORDER BY create_time DESC
</select>
<!-- 分页查询 -->
<select id="selectByPage" parameterType="map" resultMap="userResultMap">
SELECT id, username, password, email, phone, age, gender, status,
create_time, update_time
FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
ORDER BY create_time DESC
LIMIT #{offset}, #{limit}
</select>
<!-- 查询总数 -->
<select id="countByCondition" parameterType="map" resultType="int">
SELECT COUNT(*)
FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</select>
<!-- 插入 -->
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, password, email, phone, age, gender, status)
VALUES (#{username}, #{password}, #{email}, #{phone}, #{age}, #{gender}, #{status})
</insert>
<!-- 更新 -->
<update id="update" parameterType="User">
UPDATE user
<set>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="email != null">email = #{email},</if>
<if test="phone != null">phone = #{phone},</if>
<if test="age != null">age = #{age},</if>
<if test="gender != null">gender = #{gender},</if>
<if test="status != null">status = #{status},</if>
</set>
WHERE id = #{id}
</update>
<!-- 删除 -->
<delete id="deleteById" parameterType="int">
DELETE FROM user WHERE id = #{id}
</delete>
<!-- 批量删除 -->
<delete id="deleteByIds">
DELETE FROM user WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>9. 实体类开发
package com.example.ssm.entity;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 用户实体类
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String username;
private String password;
private String email;
private String phone;
private Integer age;
private Integer gender; // 0-未知,1-男,2-女
private Integer status; // 0-禁用,1-启用
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 无参构造方法
public User() {
}
// 有参构造方法
public User(String username, String password, String email) {
this.username = username;
this.password = password;
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 String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
", age=" + age +
", gender=" + gender +
", status=" + status +
", createTime=" + createTime +
", updateTime=" + updateTime +
'}';
}
}package com.example.ssm.util;
import java.util.List;
/**
* 分页工具类
*/
public class PageInfo<T> {
private List<T> list; // 数据列表
private int pageNum; // 当前页码
private int pageSize; // 每页数量
private long total; // 总记录数
private int pages; // 总页数
public PageInfo() {
}
public PageInfo(List<T> list, int pageNum, int pageSize, long total) {
this.list = list;
this.pageNum = pageNum;
this.pageSize = pageSize;
this.total = total;
this.pages = (int) Math.ceil((double) total / pageSize);
}
// Getter和Setter方法
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
this.pages = (int) Math.ceil((double) total / pageSize);
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public boolean isHasPreviousPage() {
return pageNum > 1;
}
public boolean isHasNextPage() {
return pageNum < pages;
}
}10. Mapper层开发
package com.example.ssm.mapper;
import com.example.ssm.entity.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* 用户Mapper接口
*/
public interface UserMapper {
/**
* 根据ID查询用户
*/
User selectById(Integer id);
/**
* 查询所有用户
*/
List<User> selectAll();
/**
* 条件查询
*/
List<User> selectByCondition(Map<String, Object> params);
/**
* 分页查询
*/
List<User> selectByPage(Map<String, Object> params);
/**
* 查询总数
*/
int countByCondition(Map<String, Object> params);
/**
* 插入用户
*/
int insert(User user);
/**
* 更新用户
*/
int update(User user);
/**
* 根据ID删除用户
*/
int deleteById(Integer id);
/**
* 批量删除用户
*/
int deleteByIds(@Param("ids") List<Integer> ids);
}11. Service层开发
package com.example.ssm.service;
import com.example.ssm.entity.User;
import com.example.ssm.util.PageInfo;
import java.util.List;
import java.util.Map;
/**
* 用户服务接口
*/
public interface UserService {
/**
* 根据ID查询用户
*/
User getUserById(Integer id);
/**
* 查询所有用户
*/
List<User> getAllUsers();
/**
* 条件查询
*/
List<User> getUsersByCondition(Map<String, Object> params);
/**
* 分页查询
*/
PageInfo<User> getUsersByPage(int pageNum, int pageSize, Map<String, Object> params);
/**
* 添加用户
*/
boolean addUser(User user);
/**
* 更新用户
*/
boolean updateUser(User user);
/**
* 删除用户
*/
boolean deleteUser(Integer id);
/**
* 批量删除用户
*/
boolean deleteUsers(List<Integer> ids);
}package com.example.ssm.service.impl;
import com.example.ssm.entity.User;
import com.example.ssm.mapper.UserMapper;
import com.example.ssm.service.UserService;
import com.example.ssm.util.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 用户服务实现类
*/
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Integer id) {
return userMapper.selectById(id);
}
@Override
public List<User> getAllUsers() {
return userMapper.selectAll();
}
@Override
public List<User> getUsersByCondition(Map<String, Object> params) {
return userMapper.selectByCondition(params);
}
@Override
public PageInfo<User> getUsersByPage(int pageNum, int pageSize, Map<String, Object> params) {
// 计算偏移量
int offset = (pageNum - 1) * pageSize;
// 设置分页参数
Map<String, Object> pageParams = new HashMap<>(params);
pageParams.put("offset", offset);
pageParams.put("limit", pageSize);
// 查询数据
List<User> list = userMapper.selectByPage(pageParams);
// 查询总数
long total = userMapper.countByCondition(params);
return new PageInfo<>(list, pageNum, pageSize, total);
}
@Override
public boolean addUser(User user) {
// 设置默认值
if (user.getStatus() == null) {
user.setStatus(1);
}
if (user.getGender() == null) {
user.setGender(0);
}
return userMapper.insert(user) > 0;
}
@Override
public boolean updateUser(User user) {
return userMapper.update(user) > 0;
}
@Override
public boolean deleteUser(Integer id) {
return userMapper.deleteById(id) > 0;
}
@Override
public boolean deleteUsers(List<Integer> ids) {
return userMapper.deleteByIds(ids) > 0;
}
}12. Controller层开发
package com.example.ssm.controller;
import com.example.ssm.entity.User;
import com.example.ssm.service.UserService;
import com.example.ssm.util.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 用户控制器
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 用户列表页面
*/
@GetMapping("/list")
public String list(@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(required = false) String username,
@RequestParam(required = false) Integer status,
Model model) {
// 构建查询条件
Map<String, Object> params = new HashMap<>();
if (username != null && !username.trim().isEmpty()) {
params.put("username", username);
}
if (status != null) {
params.put("status", status);
}
// 分页查询
PageInfo<User> pageInfo = userService.getUsersByPage(pageNum, pageSize, params);
model.addAttribute("pageInfo", pageInfo);
model.addAttribute("username", username);
model.addAttribute("status", status);
return "user/list";
}
/**
* 添加用户页面
*/
@GetMapping("/add")
public String addPage() {
return "user/add";
}
/**
* 添加用户
*/
@PostMapping("/add")
@ResponseBody
public Map<String, Object> add(User user) {
Map<String, Object> result = new HashMap<>();
try {
boolean success = userService.addUser(user);
if (success) {
result.put("success", true);
result.put("message", "添加成功");
} else {
result.put("success", false);
result.put("message", "添加失败");
}
} catch (Exception e) {
result.put("success", false);
result.put("message", "添加失败:" + e.getMessage());
}
return result;
}
/**
* 编辑用户页面
*/
@GetMapping("/edit/{id}")
public String editPage(@PathVariable Integer id, Model model) {
User user = userService.getUserById(id);
model.addAttribute("user", user);
return "user/edit";
}
/**
* 更新用户
*/
@PostMapping("/update")
@ResponseBody
public Map<String, Object> update(User user) {
Map<String, Object> result = new HashMap<>();
try {
boolean success = userService.updateUser(user);
if (success) {
result.put("success", true);
result.put("message", "更新成功");
} else {
result.put("success", false);
result.put("message", "更新失败");
}
} catch (Exception e) {
result.put("success", false);
result.put("message", "更新失败:" + e.getMessage());
}
return result;
}
/**
* 删除用户
*/
@PostMapping("/delete/{id}")
@ResponseBody
public Map<String, Object> delete(@PathVariable Integer id) {
Map<String, Object> result = new HashMap<>();
try {
boolean success = userService.deleteUser(id);
if (success) {
result.put("success", true);
result.put("message", "删除成功");
} else {
result.put("success", false);
result.put("message", "删除失败");
}
} catch (Exception e) {
result.put("success", false);
result.put("message", "删除失败:" + e.getMessage());
}
return result;
}
/**
* 批量删除
*/
@PostMapping("/batchDelete")
@ResponseBody
public Map<String, Object> batchDelete(@RequestParam("ids") List<Integer> ids) {
Map<String, Object> result = new HashMap<>();
try {
boolean success = userService.deleteUsers(ids);
if (success) {
result.put("success", true);
result.put("message", "批量删除成功");
} else {
result.put("success", false);
result.put("message", "批量删除失败");
}
} catch (Exception e) {
result.put("success", false);
result.put("message", "批量删除失败:" + e.getMessage());
}
return result;
}
/**
* 根据ID查询用户(JSON)
*/
@GetMapping("/{id}")
@ResponseBody
public User getUser(@PathVariable Integer id) {
return userService.getUserById(id);
}
}13. 前端页面开发
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>用户列表</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.search-form { margin-bottom: 20px; padding: 15px; background: #f5f5f5; }
.search-form input, .search-form select { margin-right: 10px; padding: 5px; }
.btn { padding: 8px 15px; cursor: pointer; border: none; border-radius: 4px; }
.btn-primary { background: #007bff; color: white; }
.btn-success { background: #28a745; color: white; }
.btn-danger { background: #dc3545; color: white; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background-color: #4CAF50; color: white; }
tr:nth-child(even) { background-color: #f2f2f2; }
.pagination { margin-top: 20px; }
.pagination a { padding: 8px 12px; margin: 0 4px; text-decoration: none; border: 1px solid #ddd; }
.pagination a.active { background-color: #4CAF50; color: white; }
</style>
</head>
<body>
<h1>用户管理系统</h1>
<!-- 搜索表单 -->
<div class="search-form">
<form action="${pageContext.request.contextPath}/user/list" method="get">
<input type="text" name="username" placeholder="用户名" value="${username}">
<select name="status">
<option value="">全部状态</option>
<option value="1" ${status == 1 ? 'selected' : ''}>启用</option>
<option value="0" ${status == 0 ? 'selected' : ''}>禁用</option>
</select>
<button type="submit" class="btn btn-primary">搜索</button>
<a href="${pageContext.request.contextPath}/user/add" class="btn btn-success">添加用户</a>
</form>
</div>
<!-- 用户列表 -->
<table>
<thead>
<tr>
<th><input type="checkbox" id="selectAll"></th>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>手机号</th>
<th>年龄</th>
<th>性别</th>
<th>状态</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<c:forEach items="${pageInfo.list}" var="user">
<tr>
<td><input type="checkbox" name="ids" value="${user.id}"></td>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.email}</td>
<td>${user.phone}</td>
<td>${user.age}</td>
<td>
<c:choose>
<c:when test="${user.gender == 1}">男</c:when>
<c:when test="${user.gender == 2}">女</c:when>
<c:otherwise>未知</c:otherwise>
</c:choose>
</td>
<td>${user.status == 1 ? '启用' : '禁用'}</td>
<td>${user.createTime}</td>
<td>
<a href="${pageContext.request.contextPath}/user/edit/${user.id}" class="btn btn-primary">编辑</a>
<button onclick="deleteUser(${user.id})" class="btn btn-danger">删除</button>
</td>
</tr>
</c:forEach>
</tbody>
</table>
<!-- 分页 -->
<div class="pagination">
<c:if test="${pageInfo.pageNum > 1}">
<a href="?pageNum=${pageInfo.pageNum - 1}&pageSize=${pageInfo.pageSize}&username=${username}&status=${status}">上一页</a>
</c:if>
<c:forEach begin="1" end="${pageInfo.pages}" var="i">
<a href="?pageNum=${i}&pageSize=${pageInfo.pageSize}&username=${username}&status=${status}"
class="${i == pageInfo.pageNum ? 'active' : ''}">${i}</a>
</c:forEach>
<c:if test="${pageInfo.pageNum < pageInfo.pages}">
<a href="?pageNum=${pageInfo.pageNum + 1}&pageSize=${pageInfo.pageSize}&username=${username}&status=${status}">下一页</a>
</c:if>
<span>共 ${pageInfo.total} 条记录,共 ${pageInfo.pages} 页</span>
</div>
<button onclick="batchDelete()" class="btn btn-danger">批量删除</button>
<script>
function deleteUser(id) {
if (confirm('确定要删除吗?')) {
fetch('${pageContext.request.contextPath}/user/delete/' + id, {
method: 'POST'
})
.then(response => response.json())
.then(data => {
alert(data.message);
if (data.success) {
location.reload();
}
});
}
}
function batchDelete() {
const checkboxes = document.querySelectorAll('input[name="ids"]:checked');
if (checkboxes.length === 0) {
alert('请选择要删除的用户');
return;
}
const ids = Array.from(checkboxes).map(cb => cb.value);
if (confirm('确定要批量删除吗?')) {
fetch('${pageContext.request.contextPath}/user/batchDelete', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'ids=' + ids.join(',')
})
.then(response => response.json())
.then(data => {
alert(data.message);
if (data.success) {
location.reload();
}
});
}
}
document.getElementById('selectAll').addEventListener('change', function() {
const checkboxes = document.querySelectorAll('input[name="ids"]');
checkboxes.forEach(cb => cb.checked = this.checked);
});
</script>
</body>
</html><%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>添加用户</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.form-group { margin-bottom: 15px; }
label { display: inline-block; width: 100px; }
input, select { padding: 8px; width: 300px; }
.btn { padding: 10px 20px; cursor: pointer; border: none; border-radius: 4px; }
.btn-primary { background: #007bff; color: white; }
.btn-secondary { background: #6c757d; color: white; }
</style>
</head>
<body>
<h1>添加用户</h1>
<form id="userForm">
<div class="form-group">
<label>用户名:</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>邮箱:</label>
<input type="email" name="email">
</div>
<div class="form-group">
<label>手机号:</label>
<input type="text" name="phone">
</div>
<div class="form-group">
<label>年龄:</label>
<input type="number" name="age" min="0" max="150">
</div>
<div class="form-group">
<label>性别:</label>
<select name="gender">
<option value="0">未知</option>
<option value="1">男</option>
<option value="2">女</option>
</select>
</div>
<div class="form-group">
<label>状态:</label>
<select name="status">
<option value="1">启用</option>
<option value="0">禁用</option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
<a href="${pageContext.request.contextPath}/user/list" class="btn btn-secondary">返回</a>
</div>
</form>
<script>
document.getElementById('userForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const params = new URLSearchParams(formData);
fetch('${pageContext.request.contextPath}/user/add', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params
})
.then(response => response.json())
.then(data => {
alert(data.message);
if (data.success) {
location.href = '${pageContext.request.contextPath}/user/list';
}
});
});
</script>
</body>
</html><%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>编辑用户</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.form-group { margin-bottom: 15px; }
label { display: inline-block; width: 100px; }
input, select { padding: 8px; width: 300px; }
.btn { padding: 10px 20px; cursor: pointer; border: none; border-radius: 4px; }
.btn-primary { background: #007bff; color: white; }
.btn-secondary { background: #6c757d; color: white; }
</style>
</head>
<body>
<h1>编辑用户</h1>
<form id="userForm">
<input type="hidden" name="id" value="${user.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="password" name="password" placeholder="留空则不修改">
</div>
<div class="form-group">
<label>邮箱:</label>
<input type="email" name="email" value="${user.email}">
</div>
<div class="form-group">
<label>手机号:</label>
<input type="text" name="phone" value="${user.phone}">
</div>
<div class="form-group">
<label>年龄:</label>
<input type="number" name="age" value="${user.age}" min="0" max="150">
</div>
<div class="form-group">
<label>性别:</label>
<select name="gender">
<option value="0" ${user.gender == 0 ? 'selected' : ''}>未知</option>
<option value="1" ${user.gender == 1 ? 'selected' : ''}>男</option>
<option value="2" ${user.gender == 2 ? 'selected' : ''}>女</option>
</select>
</div>
<div class="form-group">
<label>状态:</label>
<select name="status">
<option value="1" ${user.status == 1 ? 'selected' : ''}>启用</option>
<option value="0" ${user.status == 0 ? 'selected' : ''}>禁用</option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">更新</button>
<a href="${pageContext.request.contextPath}/user/list" class="btn btn-secondary">返回</a>
</div>
</form>
<script>
document.getElementById('userForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const params = new URLSearchParams(formData);
fetch('${pageContext.request.contextPath}/user/update', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params
})
.then(response => response.json())
.then(data => {
alert(data.message);
if (data.success) {
location.href = '${pageContext.request.contextPath}/user/list';
}
});
});
</script>
</body>
</html>14. 项目测试
- 使用Maven Tomcat插件启动:
mvn tomcat7:run- 使用IDE启动:
- 配置Tomcat服务器
- 部署项目
- 启动服务器
- 用户列表:http://localhost:8080/ssm-demo/user/list
- 添加用户:http://localhost:8080/ssm-demo/user/add
- 编辑用户:http://localhost:8080/ssm-demo/user/edit/1
- 添加用户:测试添加功能是否正常
- 查询用户:测试列表查询和条件查询
- 编辑用户:测试更新功能
- 删除用户:测试单个删除和批量删除
- 分页功能:测试分页是否正常
15. 项目部署
mvn clean package- 将生成的
ssm-demo.war文件复制到Tomcat的webapps目录 - 启动Tomcat服务器
- 访问应用
15.3 生产环境配置
- 修改数据库连接配置
- 配置日志级别
- 优化连接池参数
- 配置静态资源缓存
16. 常见问题解决
问题:页面显示中文乱码
解决:
- 确保JSP页面编码为UTF-8
- 配置字符编码过滤器
- 数据库连接URL添加
characterEncoding=utf8
问题:访问页面404
解决:
- 检查web.xml配置
- 检查Controller的@RequestMapping路径
- 检查视图解析器配置
问题:无法连接数据库
解决:
- 检查数据库服务是否启动
- 检查数据库连接配置
- 检查数据库用户权限
问题:Mapper XML文件找不到
解决:
- 检查mapperLocations配置
- 确保XML文件在resources目录下
- 检查namespace是否正确
17. 总结与进阶
- ✅ 配置文件:Spring、SpringMVC、MyBatis配置要正确
- ✅ 依赖注入:使用@Autowired注入依赖
- ✅ 事务管理:Service层添加@Transactional
- ✅ 异常处理:统一异常处理机制
- ✅ 参数绑定:SpringMVC自动参数绑定
- Spring Boot:简化SSM配置
- Spring Security:安全框架
- Redis缓存:提升性能
- 消息队列:异步处理
- 微服务架构:Spring Cloud
- 分层清晰,职责分明
- 使用事务管理保证数据一致性
- 合理使用缓存提升性能
- 统一异常处理
- 日志记录重要操作
结语
通过本教程的学习,你已经掌握了SSM框架的完整整合过程。SSM框架是Java企业级开发的基础,掌握它对于Java开发者来说非常重要。
记住:
- 多实践:通过实际项目加深理解
- 理解原理:理解框架的工作原理
- 持续学习:关注框架的新特性
- 代码规范:遵循最佳实践
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。