📚 最终完整教程:Spring Boot + MyBatis + PageHelper 全面整合(适配 JsonResult + 分页)

这是一份可直接运行的生产级教程,基于你项目的 JsonResult 风格和 PageHelper 分页,从零开始完整实现基于 YAML 配置的 CRUD + 分页功能。

📁 一、项目完整结构

demo-user-management
├── src
│   └── main
│       ├── java
│       │   └── com/example/demo
│       │       ├── DemoApplication.java              # 启动类
│       │       ├── common
│       │       │   └── JsonResult.java               # 统一返回结果
│       │       ├── config
│       │       │   └── PageHelperConfig.java         # PageHelper 配置类
│       │       ├── controller
│       │       │   └── UserController.java           # 控制器
│       │       ├── entity
│       │       │   └── User.java                     # 实体类
│       │       ├── mapper
│       │       │   └── UserMapper.java               # Mapper 接口
│       │       └── service
│       │           ├── UserService.java              # 服务接口
│       │           └── impl
│       │               └── UserServiceImpl.java      # 服务实现
│       └── resources
│           ├── application.yml                       # YAML 配置文件
│           └── mapper
│               └── UserMapper.xml                    # MyBatis XML 映射
└── pom.xml                                           # Maven 依赖

🗄️ 二、数据库准备

在 MySQL 中执行以下 SQL:

-- 创建数据库
CREATE DATABASE IF NOT EXISTS demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE demo_db;

-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    name VARCHAR(50) NOT NULL COMMENT '姓名',
    email VARCHAR(100) UNIQUE NOT NULL COMMENT '邮箱',
    age INT COMMENT '年龄',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 插入测试数据(20条,方便测试分页)
INSERT INTO users (name, email, age) VALUES 
('张伟', 'zhangwei@example.com', 25),
('李娜', 'lina@example.com', 30),
('王芳', 'wangfang@example.com', 28),
('刘洋', 'liuyang@example.com', 35),
('陈静', 'chenjing@example.com', 22),
('赵敏', 'zhaomin@example.com', 27),
('孙强', 'sunqiang@example.com', 33),
('周洁', 'zhoujie@example.com', 29),
('吴浩', 'wuhao@example.com', 31),
('郑爽', 'zhengshuang@example.com', 24),
('钱华', 'qianhua@example.com', 26),
('冯丽', 'fengli@example.com', 32),
('蒋明', 'jiangming@example.com', 28),
('沈月', 'shenyue@example.com', 23),
('韩雪', 'hanxue@example.com', 27),
('杨光', 'yangguang@example.com', 34),
('朱婷', 'zhuting@example.com', 26),
('秦岚', 'qinlan@example.com', 29),
('尤娜', 'youna@example.com', 31),
('许静', 'xujing@example.com', 24);

📦 三、Maven 依赖配置(pom.xml)

<?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
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo-user-management</artifactId>
    <version>1.0.0</version>
    <name>demo-user-management</name>
    <description>用户管理完整示例</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- MyBatis Spring Boot Starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>

        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- PageHelper 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.3.3</version>
        </dependency>

        <!-- PageHelper 依赖的 SQL 解析器 -->
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>4.7</version>
        </dependency>

        <!-- 热部署(开发工具) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- 单元测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <!-- 确保 XML 文件被正确打包 -->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

⚙️ 四、YAML 配置文件(application.yml)

src/main/resources/application.yml 中完整配置:

# ==================== Spring Boot 核心配置 ====================
spring:
  # 数据源配置
  datasource:
    url: jdbc:mysql://localhost:3306/demo_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8&allowPublicKeyRetrieval=true
    username: root
    password: 123456  # ⚠️ 请替换为你的实际密码
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 连接池配置(HikariCP)
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      idle-timeout: 30000
      connection-timeout: 30000
      max-lifetime: 1800000

  # 应用名称
  application:
    name: demo-user-management

# ==================== MyBatis 配置 ====================
mybatis:
  # 实体类别名包(XML 中 resultType 可省略包名)
  type-aliases-package: com.example.demo.entity
  # Mapper XML 文件位置
  mapper-locations: classpath:mapper/*.xml
  # MyBatis 全局配置
  configuration:
    # 开启驼峰命名自动映射(user_name -> userName)
    map-underscore-to-camel-case: true
    # 控制台打印 SQL 日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 开启懒加载
    lazy-loading-enabled: true
    # 开启二级缓存
    cache-enabled: true
    # 设置超时时间(秒)
    default-statement-timeout: 30

# ==================== PageHelper 分页插件配置 ====================
# 注意:Spring Boot 2.4+ 可能不自动加载此配置,需要配合 Java 配置类
pagehelper:
  helper-dialect: mysql
  reasonable: true
  support-methods-arguments: true
  params: count=countSql

# ==================== 日志配置 ====================
logging:
  level:
    # 显示 Mapper 接口执行的 SQL
    com.example.demo.mapper: DEBUG
    # 显示 SQL 参数绑定
    org.mybatis: DEBUG
    # 显示 PageHelper 分页信息
    com.github.pagehelper: DEBUG
  pattern:
    console: '%d{yyyy-MM-dd HH:mm:ss} - %msg%n'

# ==================== 服务器配置 ====================
server:
  port: 8080
  servlet:
    context-path: /
  # 优雅关闭
  shutdown: graceful

📄 五、统一返回结果类(JsonResult)

完全沿用你项目的设计风格,基于 HashMap 实现灵活的数据存储,支持链式调用:

package com.example.demo.common;

import java.io.Serializable;
import java.util.HashMap;

/**
 * 统一返回结果类
 * 
 * <p>用法示例:
 * <pre>
 * // 成功返回数据
 * return JsonResult.success(pageInfo);
 * 
 * // 成功返回自定义消息
 * return JsonResult.success(pageInfo, "查询成功");
 * 
 * // 链式追加额外字段
 * return JsonResult.success(pageInfo).put("extra", "extraValue");
 * 
 * // 失败返回
 * return JsonResult.fail("用户不存在");
 * </pre>
 * 
 * @author System
 * @version 1.0
 */
public class JsonResult extends HashMap<String, Object> implements Serializable {

    private static final long serialVersionUID = 1L;

    /** 成功状态码 */
    public static final int CODE_SUCCESS = 200;
    /** 失败状态码 */
    public static final int CODE_FAIL = 0;

    public JsonResult() {
        // 默认构造
    }

    /**
     * 构建成功返回结果
     * 
     * @param data 数据体(可以是 PageInfo、List、Object 等)
     * @return JsonResult
     */
    public static JsonResult success(Object data) {
        JsonResult result = new JsonResult();
        result.put("code", CODE_SUCCESS);
        result.put("msg", "操作成功");
        result.put("data", data);
        return result;
    }

    /**
     * 构建成功返回结果(自定义消息)
     * 
     * @param data 数据体
     * @param msg 自定义成功消息
     * @return JsonResult
     */
    public static JsonResult success(Object data, String msg) {
        JsonResult result = new JsonResult();
        result.put("code", CODE_SUCCESS);
        result.put("msg", msg);
        result.put("data", data);
        return result;
    }

    /**
     * 构建失败返回结果
     * 
     * @param msg 错误信息
     * @return JsonResult
     */
    public static JsonResult fail(String msg) {
        JsonResult result = new JsonResult();
        result.put("code", CODE_FAIL);
        result.put("msg", msg);
        result.put("data", null);
        return result;
    }

    /**
     * 构建失败返回结果(自定义错误码)
     * 
     * @param code 错误码
     * @param msg 错误信息
     * @return JsonResult
     */
    public static JsonResult fail(int code, String msg) {
        JsonResult result = new JsonResult();
        result.put("code", code);
        result.put("msg", msg);
        result.put("data", null);
        return result;
    }

    /**
     * 链式追加自定义字段(覆盖 HashMap 的 put 方法)
     * 
     * @param key 键
     * @param value 值
     * @return JsonResult(支持链式调用)
     */
    @Override
    public JsonResult put(String key, Object value) {
        super.put(key, value);
        return this;
    }

    /**
     * 判断是否成功
     * 
     * @return true 表示成功
     */
    public boolean isSuccess() {
        Object code = this.get("code");
        if (code == null) {
            return false;
        }
        return CODE_SUCCESS == (int) code;
    }

    /**
     * 快速获取 data 字段
     */
    public Object getData() {
        return this.get("data");
    }
}

🧩 六、实体类(User.java)

package com.example.demo.entity;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 用户实体类(对应数据库 users 表)
 * 
 * @author System
 */
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer id;
    private String name;
    private String email;
    private Integer age;
    private LocalDateTime createTime;

    // ===== 构造方法 =====
    public User() {}

    public User(String name, String email, Integer age) {
        this.name = name;
        this.email = email;
        this.age = age;
    }

    // ===== Getter & Setter =====
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    // ===== toString =====
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                ", createTime=" + createTime +
                '}';
    }
}

🔧 七、PageHelper 配置类(解决 YAML 不生效问题)

package com.example.demo.config;

import com.github.pagehelper.PageInterceptor;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

/**
 * PageHelper 分页插件配置类
 * 
 * <p>由于 Spring Boot 2.4+ 可能无法自动读取 application.yml 中的 pagehelper 配置,
 * 通过 Java 配置类手动创建 PageInterceptor Bean,确保分页插件生效。
 * 
 * @author System
 */
@Configuration
public class PageHelperConfig {

    /**
     * 创建 PageHelper 拦截器
     */
    @Bean
    public Interceptor pageHelperInterceptor() {
        PageInterceptor pageInterceptor = new PageInterceptor();

        Properties properties = new Properties();
        // 设置数据库方言为 MySQL
        properties.setProperty("helperDialect", "mysql");
        // 分页参数合理化:pageNum <= 0 时查第一页,pageNum > 总页数时查最后一页
        properties.setProperty("reasonable", "true");
        // 支持通过 Mapper 接口参数传递分页参数
        properties.setProperty("supportMethodsArguments", "true");
        // 分页参数映射
        properties.setProperty("params", "count=countSql");
        // 自动分页 COUNT 查询优化
        properties.setProperty("autoRuntimeDialect", "false");

        pageInterceptor.setProperties(properties);
        return pageInterceptor;
    }
}

🗺️ 八、Mapper 接口(UserMapper.java)

package com.example.demo.mapper;

import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 用户 Mapper 接口
 * 
 * @author System
 */
@Mapper
public interface UserMapper {

    // =============================================
    // 查询操作
    // =============================================

    /**
     * 查询所有用户
     * 
     * @return 用户列表
     */
    List<User> findAll();

    /**
     * 根据 ID 查询用户
     * 
     * @param id 用户 ID
     * @return 用户对象
     */
    User findById(@Param("id") Integer id);

    /**
     * 按姓名模糊查询(用于分页)
     * 
     * @param name 姓名(模糊匹配)
     * @return 用户列表
     */
    List<User> findByNameLike(@Param("name") String name);

    /**
     * 条件查询(支持多条件,用于复杂分页)
     * 
     * @param name 姓名(模糊匹配)
     * @param minAge 最小年龄
     * @param maxAge 最大年龄
     * @return 用户列表
     */
    List<User> findByCondition(@Param("name") String name,
                                @Param("minAge") Integer minAge,
                                @Param("maxAge") Integer maxAge);

    // =============================================
    // 新增操作
    // =============================================

    /**
     * 新增用户(返回自增主键)
     * 
     * @param user 用户对象
     * @return 影响行数
     */
    int insert(User user);

    // =============================================
    // 修改操作
    // =============================================

    /**
     * 更新用户信息
     * 
     * @param user 用户对象
     * @return 影响行数
     */
    int update(User user);

    // =============================================
    // 删除操作
    // =============================================

    /**
     * 根据 ID 删除用户
     * 
     * @param id 用户 ID
     * @return 影响行数
     */
    int deleteById(@Param("id") Integer id);

    /**
     * 批量删除用户
     * 
     * @param ids ID 列表
     * @return 影响行数
     */
    int deleteBatch(@Param("ids") List<Integer> ids);
}

📝 九、MyBatis XML 映射文件(UserMapper.xml)

src/main/resources/mapper/UserMapper.xml 中编写 SQL:

<?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.demo.mapper.UserMapper">

    <!-- ============================================= -->
    <!-- 结果映射                                         -->
    <!-- ============================================= -->
    <resultMap id="userResultMap" type="com.example.demo.entity.User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="email" column="email"/>
        <result property="age" column="age"/>
        <result property="createTime" column="create_time"/>
    </resultMap>

    <!-- 通用查询列(提高可维护性) -->
    <sql id="baseColumns">
        id, name, email, age, create_time
    </sql>

    <!-- ============================================= -->
    <!-- 1. 查询所有用户                                  -->
    <!-- ============================================= -->
    <select id="findAll" resultMap="userResultMap">
        SELECT <include refid="baseColumns"/>
        FROM users
        ORDER BY id
    </select>

    <!-- ============================================= -->
    <!-- 2. 根据 ID 查询用户                             -->
    <!-- ============================================= -->
    <select id="findById" resultMap="userResultMap">
        SELECT <include refid="baseColumns"/>
        FROM users
        WHERE id = #{id}
    </select>

    <!-- ============================================= -->
    <!-- 3. 按姓名模糊查询(分页用)                        -->
    <!-- ============================================= -->
    <select id="findByNameLike" resultMap="userResultMap">
        SELECT <include refid="baseColumns"/>
        FROM users
        <where>
            <if test="name != null and name != ''">
                name LIKE CONCAT('%', #{name}, '%')
            </if>
        </where>
        ORDER BY id
    </select>

    <!-- ============================================= -->
    <!-- 4. 条件查询(多条件分页)                         -->
    <!-- ============================================= -->
    <select id="findByCondition" resultMap="userResultMap">
        SELECT <include refid="baseColumns"/>
        FROM users
        <where>
            <if test="name != null and name != ''">
                AND name LIKE CONCAT('%', #{name}, '%')
            </if>
            <if test="minAge != null">
                AND age &gt;= #{minAge}
            </if>
            <if test="maxAge != null">
                AND age &lt;= #{maxAge}
            </if>
        </where>
        ORDER BY id
    </select>

    <!-- ============================================= -->
    <!-- 5. 新增用户(返回自增主键)                       -->
    <!-- ============================================= -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users (name, email, age)
        VALUES (#{name}, #{email}, #{age})
    </insert>

    <!-- ============================================= -->
    <!-- 6. 更新用户(动态更新)                          -->
    <!-- ============================================= -->
    <update id="update">
        UPDATE users
        <set>
            <if test="name != null and name != ''">
                name = #{name},
            </if>
            <if test="email != null and email != ''">
                email = #{email},
            </if>
            <if test="age != null">
                age = #{age},
            </if>
        </set>
        WHERE id = #{id}
    </update>

    <!-- ============================================= -->
    <!-- 7. 根据 ID 删除用户                             -->
    <!-- ============================================= -->
    <delete id="deleteById">
        DELETE FROM users
        WHERE id = #{id}
    </delete>

    <!-- ============================================= -->
    <!-- 8. 批量删除用户                                 -->
    <!-- ============================================= -->
    <delete id="deleteBatch">
        DELETE FROM users
        WHERE id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>

</mapper>

💼 十、Service 层实现

UserService.java(接口)

package com.example.demo.service;

import com.example.demo.entity.User;
import com.github.pagehelper.PageInfo;

import java.util.List;

/**
 * 用户服务接口
 * 
 * @author System
 */
public interface UserService {

    // =============================================
    // 基础 CRUD
    // =============================================

    /**
     * 查询所有用户
     */
    List<User> findAll();

    /**
     * 根据 ID 查询用户
     */
    User findById(Integer id);

    /**
     * 新增用户
     */
    int insert(User user);

    /**
     * 更新用户
     */
    int update(User user);

    /**
     * 删除用户
     */
    int deleteById(Integer id);

    /**
     * 批量删除用户
     */
    int deleteBatch(List<Integer> ids);

    // =============================================
    // 分页查询(返回 PageInfo)
    // =============================================

    /**
     * 分页查询所有用户
     * 
     * @param pageNo 当前页码(从 1 开始)
     * @param pageSize 每页条数
     * @return PageInfo 分页对象
     */
    PageInfo<User> queryPageList(int pageNo, int pageSize);

    /**
     * 按姓名分页查询
     * 
     * @param name 姓名(模糊查询)
     * @param pageNo 当前页码
     * @param pageSize 每页条数
     * @return PageInfo 分页对象
     */
    PageInfo<User> queryPageListByName(String name, int pageNo, int pageSize);

    /**
     * 多条件分页查询
     * 
     * @param name 姓名(模糊查询)
     * @param minAge 最小年龄
     * @param maxAge 最大年龄
     * @param pageNo 当前页码
     * @param pageSize 每页条数
     * @return PageInfo 分页对象
     */
    PageInfo<User> queryPageListByCondition(String name, Integer minAge, Integer maxAge,
                                             int pageNo, int pageSize);
}

UserServiceImpl.java(实现类)

package com.example.demo.service.impl;

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 用户服务实现类
 * 
 * @author System
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    // =============================================
    // 基础 CRUD 实现
    // =============================================

    @Override
    public List<User> findAll() {
        return userMapper.findAll();
    }

    @Override
    public User findById(Integer id) {
        return userMapper.findById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insert(User user) {
        return userMapper.insert(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(User user) {
        return userMapper.update(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteById(Integer id) {
        return userMapper.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteBatch(List<Integer> ids) {
        if (ids == null || ids.isEmpty()) {
            return 0;
        }
        return userMapper.deleteBatch(ids);
    }

    // =============================================
    // 分页查询实现(核心)
    // =============================================

    @Override
    public PageInfo<User> queryPageList(int pageNo, int pageSize) {
        // 1. 开启分页(必须在查询前调用)
        PageHelper.startPage(pageNo, pageSize);
        // 2. 执行查询
        List<User> userList = userMapper.findAll();
        // 3. 返回 PageInfo
        return new PageInfo<>(userList);
    }

    @Override
    public PageInfo<User> queryPageListByName(String name, int pageNo, int pageSize) {
        PageHelper.startPage(pageNo, pageSize);
        List<User> userList = userMapper.findByNameLike(name);
        return new PageInfo<>(userList);
    }

    @Override
    public PageInfo<User> queryPageListByCondition(String name, Integer minAge, Integer maxAge,
                                                    int pageNo, int pageSize) {
        PageHelper.startPage(pageNo, pageSize);
        List<User> userList = userMapper.findByCondition(name, minAge, maxAge);
        return new PageInfo<>(userList);
    }
}

🌐 十一、Controller 层(完全适配你的项目风格)

package com.example.demo.controller;

import com.example.demo.common.JsonResult;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * 用户管理控制器
 * 
 * <p>完全适配项目现有风格:
 * <ul>
 *   <li>统一使用 POST 请求接收 Map 参数</li>
 *   <li>统一返回 JsonResult</li>
 *   <li>分页使用 PageHelper + PageInfo</li>
 * </ul>
 * 
 * @author System
 */
@RestController
@RequestMapping("/api/users")
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private UserService userService;

    // =============================================
    // 1. 分页查询(核心接口)
    // =============================================

    /**
     * 分页查询用户列表
     * 
     * <p>请求示例:
     * <pre>
     * POST /api/users/page
     * {
     *     "pageNo": 1,
     *     "pageSize": 5,
     *     "name": "张"
     * }
     * </pre>
     * 
     * @param params 请求参数
     * @return JsonResult 包含 PageInfo 的分页数据
     */
    @PostMapping("/page")
    public JsonResult page(@RequestBody Map<String, Object> params) {
        try {
            // 1. 提取参数(与你项目写法完全一致)
            String name = params.get("name") != null ? params.get("name").toString() : null;
            int pageNo = Integer.parseInt(params.get("pageNo").toString());
            int pageSize = Integer.parseInt(params.get("pageSize").toString());

            logger.info("分页查询用户:pageNo={}, pageSize={}, name={}", pageNo, pageSize, name);

            // 2. 调用 Service
            PageInfo<User> pageInfo = userService.queryPageListByName(name, pageNo, pageSize);

            // 3. 返回成功结果(直接将 PageInfo 放入 data)
            return JsonResult.success(pageInfo);

        } catch (NumberFormatException e) {
            logger.error("分页参数格式错误", e);
            return JsonResult.fail("分页参数格式错误:pageNo 和 pageSize 必须为数字");
        } catch (Exception e) {
            logger.error("查询用户列表失败", e);
            return JsonResult.fail("查询用户列表失败:" + e.getMessage());
        }
    }

    /**
     * 多条件分页查询
     */
    @PostMapping("/page/condition")
    public JsonResult pageByCondition(@RequestBody Map<String, Object> params) {
        try {
            String name = params.get("name") != null ? params.get("name").toString() : null;
            Integer minAge = params.get("minAge") != null ? Integer.parseInt(params.get("minAge").toString()) : null;
            Integer maxAge = params.get("maxAge") != null ? Integer.parseInt(params.get("maxAge").toString()) : null;
            int pageNo = Integer.parseInt(params.get("pageNo").toString());
            int pageSize = Integer.parseInt(params.get("pageSize").toString());

            PageInfo<User> pageInfo = userService.queryPageListByCondition(name, minAge, maxAge, pageNo, pageSize);
            return JsonResult.success(pageInfo);

        } catch (Exception e) {
            logger.error("多条件查询失败", e);
            return JsonResult.fail("查询失败:" + e.getMessage());
        }
    }

    // =============================================
    // 2. 新增用户
    // =============================================

    @PostMapping("/add")
    public JsonResult add(@RequestBody Map<String, Object> params) {
        try {
            // 参数校验
            String name = params.get("name") != null ? params.get("name").toString() : null;
            String email = params.get("email") != null ? params.get("email").toString() : null;
            String ageStr = params.get("age") != null ? params.get("age").toString() : null;

            if (name == null || name.trim().isEmpty()) {
                return JsonResult.fail("姓名不能为空");
            }
            if (email == null || email.trim().isEmpty()) {
                return JsonResult.fail("邮箱不能为空");
            }

            User user = new User();
            user.setName(name.trim());
            user.setEmail(email.trim());
            user.setAge(ageStr != null ? Integer.parseInt(ageStr) : null);

            int result = userService.insert(user);
            if (result > 0) {
                return JsonResult.success(user, "新增成功");
            } else {
                return JsonResult.fail("新增失败");
            }

        } catch (NumberFormatException e) {
            return JsonResult.fail("年龄格式错误");
        } catch (Exception e) {
            logger.error("新增用户失败", e);
            return JsonResult.fail("新增用户失败:" + e.getMessage());
        }
    }

    // =============================================
    // 3. 修改用户
    // =============================================

    @PostMapping("/update")
    public JsonResult update(@RequestBody Map<String, Object> params) {
        try {
            Integer id = params.get("id") != null ? Integer.parseInt(params.get("id").toString()) : null;
            if (id == null) {
                return JsonResult.fail("用户 ID 不能为空");
            }

            User user = new User();
            user.setId(id);
            if (params.get("name") != null) {
                user.setName(params.get("name").toString());
            }
            if (params.get("email") != null) {
                user.setEmail(params.get("email").toString());
            }
            if (params.get("age") != null) {
                user.setAge(Integer.parseInt(params.get("age").toString()));
            }

            int result = userService.update(user);
            if (result > 0) {
                return JsonResult.success(null, "修改成功");
            } else {
                return JsonResult.fail("修改失败,用户不存在");
            }

        } catch (NumberFormatException e) {
            return JsonResult.fail("参数格式错误");
        } catch (Exception e) {
            logger.error("修改用户失败", e);
            return JsonResult.fail("修改用户失败:" + e.getMessage());
        }
    }

    // =============================================
    // 4. 删除用户
    // =============================================

    @PostMapping("/delete")
    public JsonResult delete(@RequestBody Map<String, Object> params) {
        try {
            Integer id = params.get("id") != null ? Integer.parseInt(params.get("id").toString()) : null;
            if (id == null) {
                return JsonResult.fail("用户 ID 不能为空");
            }

            int result = userService.deleteById(id);
            if (result > 0) {
                return JsonResult.success(null, "删除成功");
            } else {
                return JsonResult.fail("删除失败,用户不存在");
            }

        } catch (NumberFormatException e) {
            return JsonResult.fail("ID 格式错误");
        } catch (Exception e) {
            logger.error("删除用户失败", e);
            return JsonResult.fail("删除用户失败:" + e.getMessage());
        }
    }

    /**
     * 批量删除用户
     */
    @PostMapping("/delete/batch")
    public JsonResult deleteBatch(@RequestBody Map<String, Object> params) {
        try {
            @SuppressWarnings("unchecked")
            List<Integer> ids = (List<Integer>) params.get("ids");
            if (ids == null || ids.isEmpty()) {
                return JsonResult.fail("请选择要删除的用户");
            }

            int result = userService.deleteBatch(ids);
            return JsonResult.success(result, "成功删除 " + result + " 条记录");

        } catch (Exception e) {
            logger.error("批量删除失败", e);
            return JsonResult.fail("批量删除失败:" + e.getMessage());
        }
    }

    // =============================================
    // 5. 查询用户详情
    // =============================================

    @PostMapping("/info")
    public JsonResult info(@RequestBody Map<String, Object> params) {
        try {
            Integer id = params.get("id") != null ? Integer.parseInt(params.get("id").toString()) : null;
            if (id == null) {
                return JsonResult.fail("用户 ID 不能为空");
            }

            User user = userService.findById(id);
            if (user != null) {
                return JsonResult.success(user, "查询成功");
            } else {
                return JsonResult.fail("用户不存在");
            }

        } catch (NumberFormatException e) {
            return JsonResult.fail("ID 格式错误");
        } catch (Exception e) {
            logger.error("查询用户详情失败", e);
            return JsonResult.fail("查询失败:" + e.getMessage());
        }
    }

    // =============================================
    // 6. 查询所有用户(不分页)
    // =============================================

    @PostMapping("/all")
    public JsonResult all() {
        try {
            List<User> userList = userService.findAll();
            return JsonResult.success(userList);
        } catch (Exception e) {
            logger.error("查询所有用户失败", e);
            return JsonResult.fail("查询失败:" + e.getMessage());
        }
    }
}

🚀 十二、启动类(DemoApplication.java)

package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * Spring Boot 启动类
 * 
 * @author System
 */
@SpringBootApplication
@EnableTransactionManagement  // 开启事务管理
@MapperScan("com.example.demo.mapper")  // 扫描 Mapper 接口
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
        System.out.println("========================================");
        System.out.println("  🚀 用户管理服务启动成功!");
        System.out.println("  📍 访问地址:http://localhost:8080");
        System.out.println("========================================");
    }
}

🧪 十三、接口测试指南

1. 分页查询用户

请求地址POST http://localhost:8080/api/users/page

请求 Body(JSON)

{
    "pageNo": 1,
    "pageSize": 5,
    "name": "张"
}

返回结果

{
    "code": 200,
    "msg": "操作成功",
    "data": {
        "total": 2,
        "list": [
            {"id": 1, "name": "张伟", "email": "zhangwei@example.com", "age": 25, "createTime": "2026-07-17T10:00:00"},
            {"id": 19, "name": "张伟", "email": "zhangwei_dup@example.com", "age": 26, "createTime": "2026-07-17T10:00:00"}
        ],
        "pageNum": 1,
        "pageSize": 5,
        "pages": 1,
        "isFirstPage": true,
        "isLastPage": true
        // ... PageInfo 其他属性
    }
}

2. 多条件分页查询

请求地址POST http://localhost:8080/api/users/page/condition

{
    "pageNo": 1,
    "pageSize": 10,
    "name": "张",
    "minAge": 20,
    "maxAge": 30
}

3. 新增用户

请求地址POST http://localhost:8080/api/users/add

{
    "name": "赵六",
    "email": "zhaoliu@example.com",
    "age": 26
}

4. 修改用户

请求地址POST http://localhost:8080/api/users/update

{
    "id": 1,
    "name": "张伟更新",
    "age": 26
}

5. 删除用户

请求地址POST http://localhost:8080/api/users/delete

{
    "id": 6
}

6. 批量删除用户

请求地址POST http://localhost:8080/api/users/delete/batch

{
    "ids": [7, 8, 9]
}

7. 查询用户详情

请求地址POST http://localhost:8080/api/users/info

{
    "id": 1
}

8. 查询所有用户

请求地址POST http://localhost:8080/api/users/all

🔧 十四、常见问题解决方案

问题现象 可能原因 解决方案
PageHelper 分页不生效 YAML 配置未被 Spring Boot 自动加载 ✅ 按照教程第7步,添加 PageHelperConfig 配置类
找不到 Mapper 接口 未扫描到 Mapper 包 ✅ 在启动类添加 @MapperScan("com.example.demo.mapper")
XML 文件无法加载 路径配置错误或 XML 文件未被打包 ✅ 确保 mapper-locations: classpath:mapper/*.xml,并检查 Maven 资源配置
数据库连接失败 密码错误/URL 不正确/MySQL 未启动 ✅ 检查 application.yml 中的 passwordurl
日期类型映射异常 create_time 字段映射失败 ✅ MySQL 驱动版本 >= 8.0,或改用 java.util.Date
SQL 日志不显示 未配置 MyBatis 日志 ✅ 配置 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
参数解析异常 Controller 接收参数为空 ✅ 确保请求 Content-Type 为 application/json
事务不回滚 未开启事务管理或异常被捕获 ✅ 添加 @EnableTransactionManagement,使用 @Transactional(rollbackFor = Exception.class)
分页总记录数不对 PageHelper 版本或配置问题 ✅ 在配置类中添加 params=count=countSql 参数

📊 十五、返回数据格式说明

成功返回格式

{
    "code": 200,
    "msg": "操作成功",
    "data": "任意数据(PageInfo/List/Object/String)"
}

失败返回格式

{
    "code": 0,
    "msg": "具体错误信息",
    "data": null
}

分页返回 data 结构(PageInfo 包含的字段)

字段 类型 说明
total long 总记录数
list List 当前页数据列表
pageNum int 当前页码
pageSize int 每页条数
pages int 总页数
isFirstPage boolean 是否第一页
isLastPage boolean 是否最后一页
prePage int 上一页页码
nextPage int 下一页页码

✅ 十六、教程总结

本教程完整覆盖了以下核心内容:

序号 核心内容 实现方式
1 数据库设计 MySQL 8.0 + users 表
2 项目依赖 Maven + Spring Boot 2.7 + MyBatis + PageHelper
3 YAML 配置 数据源 + MyBatis + 日志
4 PageHelper 配置 Java 配置类(解决 YAML 不生效问题)
5 统一返回结果 JsonResult(基于 HashMap,支持链式调用)
6 实体类 User 与数据库表映射
7 Mapper 接口 @Mapper 注解 + XML 映射
8 XML 映射 resultMap + 动态 SQL + 批量操作
9 Service 层 接口 + 实现 + 事务管理
10 Controller 层 POST 请求接收 Map 参数 + 返回 JsonResult
11 分页实现 PageHelper.startPage + PageInfo
12 完整 CRUD 增、删、改、查 + 批量操作

核心代码片段汇总

分页核心实现(Service 层)

PageHelper.startPage(pageNo, pageSize);
List<User> list = userMapper.findByNameLike(name);
return new PageInfo<>(list);

统一返回(Controller 层)

PageInfo<User> pageInfo = userService.queryPageListByName(name, pageNo, pageSize);
return JsonResult.success(pageInfo);

链式追加额外字段

return JsonResult.success(pageInfo)
    .put("extra1", "value1")
    .put("extra2", "value2");

🎉 恭喜!你现在已经掌握了在 Spring Boot 中使用 YAML + MyBatis + PageHelper + JsonResult 实现完整增删改查和分页查询的全部技能! 你可以将这套代码直接复制到你的项目中使用,也可以根据实际业务需求进行扩展。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐