下面我将按照需求,创建一个完整的 Spring Boot 3 整合 MyBatis-Plus 的示例,实现规范的 CRUD 操作。
1. 首先添加 Maven 依赖 (pom.xml)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| <?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>3.1.3</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>phone-area-code</artifactId> <version>0.0.1-SNAPSHOT</version> <name>phone-area-code</name> <properties> <java.version>17</java.version> <mybatis-plus.version>3.5.3.1</mybatis-plus.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.5.0</version> </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> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
|
2. 配置文件 (application.yml)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| spring: datasource: url: jdbc:mysql://localhost:3306/your_database_name?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: password driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml global-config: db-config: logic-delete-field: idDeleted logic-delete-value: 1 logic-not-delete-value: 0 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true
|
3. 基础 PO 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package com.example.phoneareacode.po;
import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data;
import java.time.LocalDateTime;
@Data public class BasePO {
private Integer status;
private String remark;
@TableField(fill = FieldFill.INSERT) private LocalDateTime createdTime;
@TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
@TableField(fill = FieldFill.INSERT) private String createdBy;
@TableField(fill = FieldFill.INSERT_UPDATE) private String updatedBy;
@TableLogic private Integer idDeleted; }
|
4. 实体类 PhoneAreaCodePO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.example.phoneareacode.po;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode;
@Data @EqualsAndHashCode(callSuper = true) @TableName("app_phone_area_code") public class PhoneAreaCodePO extends BasePO {
@TableId(type = IdType.AUTO) private Integer id;
private String countryName;
private String areaCode;
private Integer sortOrder; }
|
5. MyBatis-Plus 自动填充处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.example.phoneareacode.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component public class MyMetaObjectHandler implements MetaObjectHandler {
@Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createdTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "updatedTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "status", Integer.class, 1); }
@Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updatedTime", LocalDateTime.class, LocalDateTime.now()); } }
|
6. 通用分页请求对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.example.phoneareacode.dto;
import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data;
@Data @Schema(description = "分页请求参数") public class PageRequest {
@Schema(description = "页码,从1开始", example = "1") private Integer pageNum = 1;
@Schema(description = "每页条数", example = "10") private Integer pageSize = 10; }
|
7. 通用响应对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package com.example.phoneareacode.common;
import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data;
@Data @Schema(description = "API响应结果") public class ApiResponse<T> {
@Schema(description = "状态码:200表示成功,其他表示失败", example = "200") private int code;
@Schema(description = "响应消息", example = "操作成功") private String message;
@Schema(description = "响应数据") private T data;
public static <T> ApiResponse<T> success(T data) { ApiResponse<T> response = new ApiResponse<>(); response.setCode(200); response.setMessage("操作成功"); response.setData(data); return response; }
public static <T> ApiResponse<T> success() { return success(null); }
public static <T> ApiResponse<T> fail(int code, String message) { ApiResponse<T> response = new ApiResponse<>(); response.setCode(code); response.setMessage(message); response.setData(null); return response; }
public static <T> ApiResponse<T> fail(String message) { return fail(500, message); } }
|
8. Mapper 接口
1 2 3 4 5 6 7 8 9 10 11 12
| package com.example.phoneareacode.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.phoneareacode.po.PhoneAreaCodePO; import org.apache.ibatis.annotations.Mapper;
@Mapper public interface PhoneAreaCodeMapper extends BaseMapper<PhoneAreaCodePO> { }
|
9. Service 接口及实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| package com.example.phoneareacode.service;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.example.phoneareacode.dto.PageRequest; import com.example.phoneareacode.po.PhoneAreaCodePO;
public interface PhoneAreaCodeService extends IService<PhoneAreaCodePO> {
IPage<PhoneAreaCodePO> getPageList(PageRequest pageRequest);
PhoneAreaCodePO getById(Integer id);
boolean save(PhoneAreaCodePO phoneAreaCodePO);
boolean update(PhoneAreaCodePO phoneAreaCodePO);
boolean removeById(Integer id); }
|
PhoneAreaCodeServiceImpl实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.example.phoneareacode.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.phoneareacode.dto.PageRequest; import com.example.phoneareacode.mapper.PhoneAreaCodeMapper; import com.example.phoneareacode.po.PhoneAreaCodePO; import com.example.phoneareacode.service.PhoneAreaCodeService; import org.springframework.stereotype.Service;
@Service public class PhoneAreaCodeServiceImpl extends ServiceImpl<PhoneAreaCodeMapper, PhoneAreaCodePO> implements PhoneAreaCodeService {
@Override public IPage<PhoneAreaCodePO> getPageList(PageRequest pageRequest) { return this.page( new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>( pageRequest.getPageNum(), pageRequest.getPageSize() ) ); }
@Override public PhoneAreaCodePO getById(Integer id) { return super.getById(id); }
@Override public boolean save(PhoneAreaCodePO phoneAreaCodePO) { return super.save(phoneAreaCodePO); }
@Override public boolean update(PhoneAreaCodePO phoneAreaCodePO) { return super.updateById(phoneAreaCodePO); }
@Override public boolean removeById(Integer id) { return super.removeById(id); } }
|
10. Controller 层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| package com.example.phoneareacode.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.example.phoneareacode.common.ApiResponse; import com.example.phoneareacode.dto.PageRequest; import com.example.phoneareacode.po.PhoneAreaCodePO; import com.example.phoneareacode.service.PhoneAreaCodeService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/api/phone-area-code") @Tag(name = "手机区号管理", description = "手机区号CRUD操作") public class PhoneAreaCodeController {
@Autowired private PhoneAreaCodeService phoneAreaCodeService;
@PostMapping("/page") @Operation(summary = "分页查询手机区号列表") public ApiResponse<IPage<PhoneAreaCodePO>> getPageList(@RequestBody PageRequest pageRequest) { return ApiResponse.success(phoneAreaCodeService.getPageList(pageRequest)); }
@PostMapping("/getById") @Operation(summary = "根据ID查询手机区号") public ApiResponse<PhoneAreaCodePO> getById(@RequestBody Integer id) { return ApiResponse.success(phoneAreaCodeService.getById(id)); }
@PostMapping("/save") @Operation(summary = "新增手机区号") public ApiResponse<Boolean> save(@RequestBody PhoneAreaCodePO phoneAreaCodePO) { return ApiResponse.success(phoneAreaCodeService.save(phoneAreaCodePO)); }
@PostMapping("/update") @Operation(summary = "更新手机区号") public ApiResponse<Boolean> update(@RequestBody PhoneAreaCodePO phoneAreaCodePO) { return ApiResponse.success(phoneAreaCodeService.update(phoneAreaCodePO)); }
@PostMapping("/delete") @Operation(summary = "删除手机区号") public ApiResponse<Boolean> delete(@RequestBody Integer id) { return ApiResponse.success(phoneAreaCodeService.removeById(id)); } }
|
11. MyBatis-Plus 配置类(分页插件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.example.phoneareacode.config;
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;
@Configuration public class MyBatisPlusConfig {
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
|
12. 启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.example.phoneareacode;
import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @MapperScan("com.example.phoneareacode.mapper") public class PhoneAreaCodeApplication {
public static void main(String[] args) { SpringApplication.run(PhoneAreaCodeApplication.class, args); } }
|
13. 数据表
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| create table `your_database_name`.`app_phone_area_code`( `id` int(8) NOT NULL AUTO_INCREMENT , `countryName` varchar(200) DEFAULT '' , `areaCode` varchar(20) DEFAULT '' , `sortOrder` int(8) DEFAULT '0' , `status` int(1) DEFAULT '1' , `remark` varchar(200) DEFAULT '' , `createdTime` timestamp , `updatedTime` timestamp , `createdBy` varchar(100) DEFAULT '' , `updatedBy` varchar(100) DEFAULT '' , `idDeleted` int(1) DEFAULT '0' , PRIMARY KEY (`id`) )
|
项目结构说明
整个项目按照标准的分层架构设计:
- PO层:存放实体类,继承基础BasePO,包含公共字段
- Mapper层:数据访问层,继承MyBatis-Plus的BaseMapper
- Service层:业务逻辑层,实现具体的业务逻辑
- Controller层:控制层,只负责接收请求和返回响应,不包含业务逻辑
- Common:存放通用响应对象等公共类
- Config:存放配置类
- DTO:存放数据传输对象,如分页请求对象
为了测试上述接口,我们可以使用Postman或编写单元测试来验证接口的正确性。下面提供两种测试方案:
一、使用Postman测试接口
所有接口都使用POST方法,以下是各接口的测试示例:
分页查询接口
- URL:
http://localhost:8080/api/phone-area-code/page - 请求体:
1 2 3 4
| { "pageNum": 1, "pageSize": 10 }
|
新增接口
- URL:
http://localhost:8080/api/phone-area-code/save - 请求体:
1 2 3 4 5 6
| { "countryName": "中国", "areaCode": "+86", "sortOrder": 100, "remark": "中国大陆地区" }
|
查询单个接口
- URL:
http://localhost:8080/api/phone-area-code/getById - 请求体:
1 (要查询的ID)
更新接口
- URL:
http://localhost:8080/api/phone-area-code/update - 请求体:
1 2 3 4 5 6 7
| { "id": 1, "countryName": "中国", "areaCode": "+86", "sortOrder": 200, "remark": "中国大陆地区手机号" }
|
删除接口
- URL:
http://localhost:8080/api/phone-area-code/delete - 请求体:
1 (要删除的ID)
二、编写单元测试
下面是使用Spring Boot Test编写的单元测试代码:
三、测试注意事项
数据库准备:
- 确保测试前已创建好数据库表结构
- 可以在测试类上添加
@Sql注解自动执行初始化SQL脚本
测试顺序:
- 建议先执行新增测试,再执行查询、更新测试
- 最后执行删除测试
测试环境:
- 可以在
application-test.yml中配置测试环境的数据库,避免影响生产数据
1 2 3 4 5
| spring: datasource: url: jdbc:mysql://localhost:3306/test_database?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: password
|
- 添加测试依赖:
如果需要更丰富的测试支持,可以在pom.xml中添加:
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>test</scope> </dependency>
|
通过以上测试方法,可以全面验证我们实现的接口功能是否正常,确保代码的质量和稳定性。
实现特点
- 统一响应格式:使用ApiResponse统一封装响应结果
- 通用分页:实现了通用的PageRequest分页请求对象
- 统一使用POST请求:所有接口都使用POST方法
- 逻辑删除:通过MyBatis-Plus实现逻辑删除功能
- 自动填充:自动填充创建时间、更新时间等公共字段
- Lombok:使用@Data等注解减少getter、setter等模板代码
- 控制层无业务逻辑:Controller只负责转发请求和响应结果
通过以上实现,我们构建了一个规范、高效的Spring Boot 3整合MyBatis-Plus的项目,实现了对手机区号表的完整CRUD操作。