一、项目结构(标准后端结构)
src/main
├── java/com/example/demo
│ ├── DemoApplication.java # 启动类
│ ├── common/
│ │ └── Result.java # 全局统一返回
│ ├── entity/ # 数据库实体
│ │ ├── User.java
│ │ └── SysUnit.java
│ ├── dto/ # 查询参数封装
│ │ └── UserQueryDTO.java
│ ├── vo/ # 视图对象(连表/返回前端)
│ │ └── UserUnitVO.java
│ ├── mapper/ # Mapper接口
│ │ └── UserMapper.java
│ ├── service/
│ │ ├── UserService.java # 业务接口
│ │ └── impl/
│ │ └── UserServiceImpl.java # 业务实现
│ └── controller/
│ └── UserController.java # 接口层
└── resources/ # 资源目录(核心!)
├── mapper/
│ └── xml/
│ └── UserMapper.xml # XML SQL文件(放这里!)
└── application.yml # 配置文件
二、核心依赖(pom.xml)
xml
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok 简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
三、配置文件(application.yml)
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/your_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath:mapper/xml/*.xml
# ✅ 修复:同时扫描 entity 和 vo
type-aliases-package: com.example.demo.entity,com.example.demo.vo
configuration:
map-unders-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
四、基础代码编写
0. 配置类
java
package com.example.demo.common;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis-Plus 分页插件配置
*/
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// MySQL分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
1. 全局统一返回结果(Result.java)
实际开发必须用统一返回体,前端方便解析
java
package com.example.demo.common;
import lombok.Data;
/**
* 全局统一返回结果
*/
@Data
public class Result<T> {
private int code;
private String msg;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMsg("操作成功");
result.setData(data);
return result;
}
public static <T> Result<T> fail(String msg) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMsg(msg);
return result;
}
}
2.1. 实体类 User.java
对应数据库表 sys_user(企业标准表名)
java
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用户实体类
*/
@Data
@TableName("sys_user") // 绑定数据库表名
public class User {
// 主键自增
@TableId(type = IdType.AUTO)
private Long id;
// 用户名
private String username;
// 单位ID(核心查询条件)
private Long unitId;
// 手机号
private String phone;
// 创建时间
private LocalDateTime createTime;
}
2.2 实体类 SysUnit.java
java
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_unit")
public class SysUnit {
@TableId(type = IdType.AUTO)
private Long id;
private String unitName; // 单位名称
private String leader; // 负责人
private LocalDateTime createTime;
}
2.3 连表查询结果VO
java
package com.example.demo.vo;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用户 + 单位 连表返回视图
*/
@Data
public class UserUnitVO {
// 用户字段
private Long id;
private String username;
private Long unitId;
private String phone;
private LocalDateTime createTime;
// 单位字段(连表查询出来的)
private String unitName; // 单位名称
private String leader; // 单位负责人
}
3. 查询参数DTO(UserQueryDTO.java)
封装查询条件,避免Controller用零散参数
java
package com.example.demo.dto;
import lombok.Data;
/**
* 用户查询参数
*/
@Data
public class UserQueryDTO {
// 用户名(模糊查询)
private String username;
// 单位ID(精确查询)
private Long unitId;
private String phone;
// 分页参数
private Integer pageNum = 1; // 默认第1页
private Integer pageSize = 10; // 默认每页10条
}
五、自定义 Mapper + XML(核心)
1. Mapper 接口(UserMapper.java)
继承 MP 的 BaseMapper,自定义业务方法
java
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.dto.UserQueryDTO;
import com.example.demo.entity.User;
import com.example.demo.vo.UserUnitVO;
import org.apache.ibatis.annotations.Param;
public interface UserMapper extends BaseMapper<User> {
// 分页查询:单表
IPage<User> selectUserList(Page<User> page, @Param("query") UserQueryDTO query);
// 分页查询:连表
IPage<UserUnitVO> selectUserUnitList(Page<UserUnitVO> page, @Param("query") UserQueryDTO query);
}
2. XML SQL 文件(UserMapper.xml)
手写自定义SQL ,放在 resources/mapper/xml/ 下
xml
<?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">
<!-- 单表查询 -->
<select id="selectUserList" resultType="User">
SELECT
id, username, unit_id, phone, create_time
FROM sys_user
<where>
<if test="query.username != null and query.username != ''">
AND username LIKE CONCAT('%', #{query.username}, '%')
</if>
<if test="query.unitId != null">
AND unit_id = #{query.unitId}
</if>
<if test="query.phone != null and query.phone != ''">
AND phone LIKE CONCAT('%', #{query.phone}, '%')
</if>
</where>
</select>
<!-- 连表查询 -->
<select id="selectUserUnitList" resultType="UserUnitVO">
SELECT
u.id,
u.username,
u.unit_id,
u.phone,
u.create_time,
un.unit_name,
un.leader
FROM sys_user u
LEFT JOIN sys_unit un
ON u.unit_id = un.id
<where>
<if test="query.username != null and query.username != ''">
AND u.username LIKE CONCAT('%', #{query.username}, '%')
</if>
<if test="query.unitId != null">
AND u.unit_id = #{query.unitId}
</if>
</where>
</select>
</mapper>
六、Service 业务层
1. Service 接口(UserService.java)
java
package com.example.demo.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.dto.UserQueryDTO;
import com.example.demo.entity.User;
import com.example.demo.vo.UserUnitVO;
public interface UserService extends IService<User> {
IPage<User> getUserList(UserQueryDTO query);
IPage<UserUnitVO> getUserUnitList(UserQueryDTO query);
}
2. Service 实现类(UserServiceImpl.java)
调用自定义 Mapper 方法(核心步骤)
java
package com.example.demo.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.dto.UserQueryDTO;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import com.example.demo.vo.UserUnitVO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Resource
private UserMapper userMapper;
@Override
public IPage<User> getUserList(UserQueryDTO query) {
// 构建分页对象
Page<User> page = new Page<>(query.getPageNum(), query.getPageSize());
return userMapper.selectUserList(page, query);
}
@Override
public IPage<UserUnitVO> getUserUnitList(UserQueryDTO query) {
Page<UserUnitVO> page = new Page<>(query.getPageNum(), query.getPageSize());
return userMapper.selectUserUnitList(page, query);
}
}
七、Controller 接口层
java
package com.example.demo.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.demo.common.Result;
import com.example.demo.dto.UserQueryDTO;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import com.example.demo.vo.UserUnitVO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
// === 分页列表接口 ===
@GetMapping("/list")
public Result<IPage<User>> getUserList(UserQueryDTO query) {
IPage<User> page = userService.getUserList(query);
return Result.success(page);
}
// === 分页连表接口 ===
@GetMapping("/unitList")
public Result<IPage<UserUnitVO>> getUserUnitList(UserQueryDTO query) {
IPage<UserUnitVO> page = userService.getUserUnitList(query);
return Result.success(page);
}
}
八、启动类
java
package com.example.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 扫描Mapper接口(核心!)
@MapperScan("com.example.demo.mapper")
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
九、测试接口
请求方式 :GET
请求地址 :http://localhost:8080/user/list?username=张三&unitId=10&pageNum=1&pageSize=10
请求参数:
username=张三(模糊匹配)
unitId=10(精确匹配)
返回结果:
json
{
"code": 200,
"msg": "操作成功",
"data": {
"records": [{}, {}], // 列表数据
"total": 125, // 总条数
"size": 10, // 每页条数
"current": 1, // 当前页
"pages": 13 // 总页数
}
}
总结
- 核心流程 :
Controller接收参数→Service调用自定义Mapper→Mapper执行XML中的SQL - 关键注解 :
@MapperScan:扫描Mapper接口@Param:XML参数绑定@TableName:实体绑定表名
- 实际开发规范 :
- 用 DTO 封装查询参数
- 用 动态SQL 拼接条件
- 用 统一返回体 封装响应
- XML 单独存放,不与代码耦合