【实战进阶】SpringBoot + MyBatis-Plus:从零到生产级项目架构深度解析
大家好,我是小白。在Web应用开发中,掌握高效的后端框架组合至关重要。本文将带你使用SpringBoot 3和MyBatis-Plus 3构建一个生产级项目,不仅涵盖基础搭建,更深入探讨分层架构设计、异常处理、性能优化等实战技巧。文末附完整项目结构与代码,建议收藏学习!
一、 开发环境与工具链(专业版配置)
1.1 版本要求
- JDK 17+(LTS版本,支持虚拟线程等新特性)
- Maven 3.8+(推荐使用Maven Wrapper,避免环境差异)
- MySQL 8.0+(支持窗口函数等高级特性)
- IDEA 2023+(社区版即可,安装Lombok插件)
1.2 环境验证
bash
# 检查JDK版本(需输出17或更高) java -version # 检查Maven版本 mvn -v # 配置Maven国内镜像(加速依赖下载) # 在~/.m2/settings.xml中添加阿里云镜像
# 检查JDK版本(需输出17或更高) java -version # 检查Maven版本 mvn -v # 配置Maven国内镜像(加速依赖下载) # 在~/.m2/settings.xml中添加阿里云镜像
图1:Maven镜像配置示意图(建议在IDEA中展示settings.xml配置)
二、 项目初始化与结构设计
2.1 使用Spring Initializr创建项目
关键配置:
- Project: Maven
- Language: Java
- Spring Boot: 3.2.x(最新稳定版)
- Group:
com.example - Artifact:
enterprise-webapp - Packaging: Jar
- Java: 17
核心依赖:
<!-- 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.5</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
xml

2.2 项目目录结构(DDD风格)
enterprise-webapp/
├── src/
│ ├── main/
│ │ ├── java/com/example/enterprise/
│ │ │ ├── config/ # 配置类
│ │ │ ├── controller/ # 控制层
│ │ │ ├── service/ # 业务层
│ │ │ │ ├── impl/ # 业务实现
│ │ │ ├── entity/ # 数据库实体
│ │ │ ├── dto/ # 数据传输对象
│ │ │ ├── vo/ # 视图对象
│ │ │ ├── mapper/ # 数据访问
│ │ │ ├── exception/ # 自定义异常
│ │ │ └── common/ # 公共工具
│ │ └── resources/
│ │ ├── application.yml # 主配置
│ │ ├── mapper/ # XML映射文件
│ │ └── static/ # 静态资源
│ └── test/ # 测试代码
└── pom.xml
text
enterprise-webapp/ ├── src/ │ ├── main/ │ │ ├── java/com/example/enterprise/ │ │ │ ├── config/ # 配置类 │ │ │ ├── controller/ # 控制层 │ │ │ ├── service/ # 业务层 │ │ │ │ ├── impl/ # 业务实现 │ │ │ ├── entity/ # 数据库实体 │ │ │ ├── dto/ # 数据传输对象 │ │ │ ├── vo/ # 视图对象 │ │ │ ├── mapper/ # 数据访问 │ │ │ ├── exception/ # 自定义异常 │ │ │ └── common/ # 公共工具 │ │ └── resources/ │ │ ├── application.yml # 主配置 │ │ ├── mapper/ # XML映射文件 │ │ └── static/ # 静态资源 │ └── test/ # 测试代码 └── pom.xml
图2:IDEA中项目结构截图(展示清晰的包分层)
三、 深度整合MyBatis-Plus
3.1 生产级配置(application.yml)
server:
port: 8080
servlet:
context-path: /api/v1 # API版本控制
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/enterprise_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: your_secure_password
# 连接池配置(HikariCP)
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
mybatis-plus:
configuration:
# 日志输出(开发环境开启,生产环境关闭)
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 驼峰命名自动转换
map-underscore-to-camel-case: true
# 全局懒加载
lazy-loading-enabled: true
type-aliases-package: com.example.enterprise.entity
mapper-locations: classpath*:mapper/**/*.xml
global-config:
db-config:
# 逻辑删除字段
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
yaml
server: port: 8080 servlet: context-path: /api/v1 # API版本控制 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/enterprise_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: your_secure_password # 连接池配置(HikariCP) hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 mybatis-plus: configuration: # 日志输出(开发环境开启,生产环境关闭) log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 驼峰命名自动转换 map-underscore-to-camel-case: true # 全局懒加载 lazy-loading-enabled: true type-aliases-package: com.example.enterprise.entity mapper-locations: classpath*:mapper/**/*.xml global-config: db-config: # 逻辑删除字段 logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0
3.2 代码生成器(解放双手)
MyBatis-Plus提供了强大的代码生成器,可以一键生成Entity、Mapper、Service、Controller。
// 在test目录下创建CodeGenerator.java
public class CodeGenerator {
public static void main(String[] args) {
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
gc.setAuthor("小白");
gc.setOpen(false);
gc.setFileOverride(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/enterprise_db?useSSL=false");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("your_password");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.example.enterprise");
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude("user", "order"); // 指定要生成的表
mpg.setStrategy(strategy);
mpg.execute();
}
}
java
// 在test目录下创建CodeGenerator.java public class CodeGenerator { public static void main(String[] args) { AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java"); gc.setAuthor("小白"); gc.setOpen(false); gc.setFileOverride(true); mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/enterprise_db?useSSL=false"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("your_password"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); pc.setParent("com.example.enterprise"); mpg.setPackageInfo(pc); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); strategy.setInclude("user", "order"); // 指定要生成的表 mpg.setStrategy(strategy); mpg.execute(); } }
图3:代码生成器执行过程截图
四、 生产级业务代码实战
4.1 数据库表设计
CREATE TABLE `user` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`email` VARCHAR(100) NOT NULL COMMENT '邮箱',
`age` TINYINT UNSIGNED DEFAULT NULL COMMENT '年龄',
`status` TINYINT DEFAULT 0 COMMENT '状态:0-正常,1-禁用',
`deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除',
`created_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`),
KEY `idx_created_time` (`created_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
sql
CREATE TABLE `user` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', `username` VARCHAR(50) NOT NULL COMMENT '用户名', `email` VARCHAR(100) NOT NULL COMMENT '邮箱', `age` TINYINT UNSIGNED DEFAULT NULL COMMENT '年龄', `status` TINYINT DEFAULT 0 COMMENT '状态:0-正常,1-禁用', `deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除', `created_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`), UNIQUE KEY `uk_email` (`email`), KEY `idx_created_time` (`created_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
4.2 实体类(Entity)设计
package com.example.enterprise.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
@Data
@TableName("user") // 指定表名
public class User {
@TableId(type = IdType.AUTO)
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度在3-20之间")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
private Integer age;
@TableLogic // 逻辑删除注解
private Integer deleted;
@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createdTime;
@TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充
private LocalDateTime updatedTime;
}
java
package com.example.enterprise.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import java.time.LocalDateTime; @Data @TableName("user") // 指定表名 public class User { @TableId(type = IdType.AUTO) private Long id; @NotBlank(message = "用户名不能为空") @Size(min = 3, max = 20, message = "用户名长度在3-20之间") private String username; @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不正确") private String email; private Integer age; @TableLogic // 逻辑删除注解 private Integer deleted; @TableField(fill = FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createdTime; @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充 private LocalDateTime updatedTime; }
4.3 DTO与VO设计(分层精髓)
// DTO:数据传输对象(接收前端请求)
@Data
public class UserCreateDTO {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式错误")
private String email;
private Integer age;
}
// VO:视图对象(返回前端数据)
@Data
public class UserVO {
private Long id;
private String username;
private String email;
private Integer age;
private String status;
private String createdTime;
}
java
// DTO:数据传输对象(接收前端请求) @Data public class UserCreateDTO { @NotBlank(message = "用户名不能为空") private String username; @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式错误") private String email; private Integer age; } // VO:视图对象(返回前端数据) @Data public class UserVO { private Long id; private String username; private String email; private Integer age; private String status; private String createdTime; }
4.4 自定义异常体系
// 基础异常
public class BusinessException extends RuntimeException {
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
}
// 用户相关异常
public class UserNotFoundException extends BusinessException {
public UserNotFoundException(Long id) {
super(404, "用户不存在: " + id);
}
public UserNotFoundException(String username) {
super(404, "用户不存在: " + username);
}
}
java
// 基础异常 public class BusinessException extends RuntimeException { private final int code; public BusinessException(int code, String message) { super(message); this.code = code; } } // 用户相关异常 public class UserNotFoundException extends BusinessException { public UserNotFoundException(Long id) { super(404, "用户不存在: " + id); } public UserNotFoundException(String username) { super(404, "用户不存在: " + username); } }
4.5 Service层(业务核心)
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(rollbackFor = Exception.class) // 事务管理
public UserVO createUser(UserCreateDTO dto) {
// 1. 业务校验
if (userMapper.selectCount(new LambdaQueryWrapper<User>()
.eq(User::getUsername, dto.getUsername())) > 0) {
throw new BusinessException(400, "用户名已存在");
}
// 2. 数据转换
User user = new User();
BeanUtils.copyProperties(dto, user);
// 3. 插入数据
if (!save(user)) {
throw new BusinessException(500, "创建用户失败");
}
// 4. 返回VO
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
vo.setStatus("正常");
vo.setCreatedTime(user.getCreatedTime().toString());
return vo;
}
@Override
public UserVO getUserById(Long id) {
User user = getById(id);
if (user == null || user.getDeleted() == 1) {
throw new UserNotFoundException(id);
}
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
vo.setStatus(user.getStatus() == 0 ? "正常" : "禁用");
return vo;
}
@Override
public PageResult<UserVO> listUsers(PageQuery query) {
// 使用MyBatis-Plus分页插件
Page<User> page = new Page<>(query.getPage(), query.getSize());
// 条件构造器
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getDeleted, 0)
.orderByDesc(User::getCreatedTime);
Page<User> userPage = userMapper.selectPage(page, wrapper);
// 转换为VO列表
List<UserVO> voList = userPage.getRecords().stream().map(user -> {
UserVO vo = new UserVO();
BeanUtils.copyProperties(user, vo);
vo.setStatus(user.getStatus() == 0 ? "正常" : "禁用");
return vo;
}).collect(Collectors.toList());
return new PageResult<>(userPage.getTotal(), voList);
}
}
java
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Autowired private UserMapper userMapper; @Override @Transactional(rollbackFor = Exception.class) // 事务管理 public UserVO createUser(UserCreateDTO dto) { // 1. 业务校验 if (userMapper.selectCount(new LambdaQueryWrapper<User>() .eq(User::getUsername, dto.getUsername())) > 0) { throw new BusinessException(400, "用户名已存在"); } // 2. 数据转换 User user = new User(); BeanUtils.copyProperties(dto, user); // 3. 插入数据 if (!save(user)) { throw new BusinessException(500, "创建用户失败"); } // 4. 返回VO UserVO vo = new UserVO(); BeanUtils.copyProperties(user, vo); vo.setStatus("正常"); vo.setCreatedTime(user.getCreatedTime().toString()); return vo; } @Override public UserVO getUserById(Long id) { User user = getById(id); if (user == null || user.getDeleted() == 1) { throw new UserNotFoundException(id); } UserVO vo = new UserVO(); BeanUtils.copyProperties(user, vo); vo.setStatus(user.getStatus() == 0 ? "正常" : "禁用"); return vo; } @Override public PageResult<UserVO> listUsers(PageQuery query) { // 使用MyBatis-Plus分页插件 Page<User> page = new Page<>(query.getPage(), query.getSize()); // 条件构造器 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getDeleted, 0) .orderByDesc(User::getCreatedTime); Page<User> userPage = userMapper.selectPage(page, wrapper); // 转换为VO列表 List<UserVO> voList = userPage.getRecords().stream().map(user -> { UserVO vo = new UserVO(); BeanUtils.copyProperties(user, vo); vo.setStatus(user.getStatus() == 0 ? "正常" : "禁用"); return vo; }).collect(Collectors.toList()); return new PageResult<>(userPage.getTotal(), voList); } }
4.6 Controller层(API入口)
@RestController
@RequestMapping("/users")
@Validated // 开启参数校验
public class UserController {
@Autowired
private UserService userService;
/**
* 创建用户
*/
@PostMapping
public ApiResult<UserVO> create(@Valid @RequestBody UserCreateDTO dto) {
return ApiResult.success(userService.createUser(dto));
}
/**
* 获取用户详情
*/
@GetMapping("/{id}")
public ApiResult<UserVO> get(@PathVariable Long id) {
return ApiResult.success(userService.getUserById(id));
}
/**
* 分页查询用户列表
*/
@GetMapping
public ApiResult<PageResult<UserVO>> list(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
PageQuery query = new PageQuery(page, size);
return ApiResult.success(userService.listUsers(query));
}
/**
* 更新用户
*/
@PutMapping("/{id}")
public ApiResult<Void> update(@PathVariable Long id, @RequestBody UserCreateDTO dto) {
// 实际业务中需要创建UpdateDTO
userService.updateUser(id, dto);
return ApiResult.success();
}
/**
* 删除用户(逻辑删除)
*/
@DeleteMapping("/{id}")
public ApiResult<Void> delete(@PathVariable Long id) {
userService.removeById(id);
return ApiResult.success();
}
}
java
@RestController @RequestMapping("/users") @Validated // 开启参数校验 public class UserController { @Autowired private UserService userService; /** * 创建用户 */ @PostMapping public ApiResult<UserVO> create(@Valid @RequestBody UserCreateDTO dto) { return ApiResult.success(userService.createUser(dto)); } /** * 获取用户详情 */ @GetMapping("/{id}") public ApiResult<UserVO> get(@PathVariable Long id) { return ApiResult.success(userService.getUserById(id)); } /** * 分页查询用户列表 */ @GetMapping public ApiResult<PageResult<UserVO>> list( @RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer size) { PageQuery query = new PageQuery(page, size); return ApiResult.success(userService.listUsers(query)); } /** * 更新用户 */ @PutMapping("/{id}") public ApiResult<Void> update(@PathVariable Long id, @RequestBody UserCreateDTO dto) { // 实际业务中需要创建UpdateDTO userService.updateUser(id, dto); return ApiResult.success(); } /** * 删除用户(逻辑删除) */ @DeleteMapping("/{id}") public ApiResult<Void> delete(@PathVariable Long id) { userService.removeById(id); return ApiResult.success(); } }
4.7 统一响应格式与全局异常处理
// 统一响应结果
@Data
public class ApiResult<T> {
private int code = 200;
private String message = "success";
private T data;
private long timestamp = System.currentTimeMillis();
public static <T> ApiResult<T> success(T data) {
ApiResult<T> result = new ApiResult<>();
result.setData(data);
return result;
}
public static <T> ApiResult<T> error(int code, String message) {
ApiResult<T> result = new ApiResult<>();
result.setCode(code);
result.setMessage(message);
return result;
}
}
// 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ApiResult<Void> handleBusinessException(BusinessException e) {
return ApiResult.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResult<Void> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return ApiResult.error(400, message);
}
@ExceptionHandler(Exception.class)
public ApiResult<Void> handleException(Exception e) {
log.error("系统异常: ", e);
return ApiResult.error(500, "系统内部错误");
}
}
java
// 统一响应结果 @Data public class ApiResult<T> { private int code = 200; private String message = "success"; private T data; private long timestamp = System.currentTimeMillis(); public static <T> ApiResult<T> success(T data) { ApiResult<T> result = new ApiResult<>(); result.setData(data); return result; } public static <T> ApiResult<T> error(int code, String message) { ApiResult<T> result = new ApiResult<>(); result.setCode(code); result.setMessage(message); return result; } } // 全局异常处理器 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ApiResult<Void> handleBusinessException(BusinessException e) { return ApiResult.error(e.getCode(), e.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public ApiResult<Void> handleValidationException(MethodArgumentNotValidException e) { String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); return ApiResult.error(400, message); } @ExceptionHandler(Exception.class) public ApiResult<Void> handleException(Exception e) { log.error("系统异常: ", e); return ApiResult.error(500, "系统内部错误"); } }
图4:Postman测试API截图(展示完整请求和响应)
五、 单元测试与质量保障
5.1 Mapper层测试
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testInsert() {
User user = new User();
user.setUsername("testuser");
user.setEmail("test@example.com");
user.setAge(25);
int result = userMapper.insert(user);
assertEquals(1, result);
assertNotNull(user.getId());
}
@Test
void testSelectById() {
User user = userMapper.selectById(1L);
assertNotNull(user);
assertEquals("testuser", user.getUsername());
}
}
java
@SpringBootTest class UserMapperTest { @Autowired private UserMapper userMapper; @Test void testInsert() { User user = new User(); user.setUsername("testuser"); user.setEmail("test@example.com"); user.setAge(25); int result = userMapper.insert(user); assertEquals(1, result); assertNotNull(user.getId()); } @Test void testSelectById() { User user = userMapper.selectById(1L); assertNotNull(user); assertEquals("testuser", user.getUsername()); } }
5.2 Service层测试(Mock)
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserMapper userMapper;
@InjectMocks
private UserServiceImpl userService;
@Test
void shouldThrowExceptionWhenUsernameExists() {
// Given
UserCreateDTO dto = new UserCreateDTO();
dto.setUsername("existing");
dto.setEmail("test@example.com");
when(userMapper.selectCount(any())).thenReturn(1);
// When & Then
assertThrows(BusinessException.class, () -> userService.createUser(dto));
}
}
java
@ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserMapper userMapper; @InjectMocks private UserServiceImpl userService; @Test void shouldThrowExceptionWhenUsernameExists() { // Given UserCreateDTO dto = new UserCreateDTO(); dto.setUsername("existing"); dto.setEmail("test@example.com"); when(userMapper.selectCount(any())).thenReturn(1); // When & Then assertThrows(BusinessException.class, () -> userService.createUser(dto)); } }
六、 性能优化与最佳实践
6.1 数据库索引优化
-- 为常用查询字段添加索引
ALTER TABLE user ADD INDEX idx_username_status (username, status);
ALTER TABLE user ADD INDEX idx_created_time (created_time);
sql
-- 为常用查询字段添加索引 ALTER TABLE user ADD INDEX idx_username_status (username, status); ALTER TABLE user ADD INDEX idx_created_time (created_time);
6.2 连接池调优
- 开发环境:连接池大小 5-10
- 生产环境:根据并发量调整,一般为 CPU核心数 * 2 + 1
6.3 MyBatis-Plus高级特性
// 条件构造器实战
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getUsername, "张") // 模糊查询
.eq(User::getStatus, 0) // 精确查询
.between(User::getAge, 18, 30) // 范围查询
.orderByDesc(User::getCreatedTime) // 排序
.last("LIMIT 10"); // 追加SQL
// 分页插件配置(在配置类中)
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
java
// 条件构造器实战 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.like(User::getUsername, "张") // 模糊查询 .eq(User::getStatus, 0) // 精确查询 .between(User::getAge, 18, 30) // 范围查询 .orderByDesc(User::getCreatedTime) // 排序 .last("LIMIT 10"); // 追加SQL // 分页插件配置(在配置类中) @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
6.4 日志与监控
# 生产环境建议配置
logging:
level:
com.example.enterprise: DEBUG
org.springframework.web: INFO
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
yaml
# 生产环境建议配置 logging: level: com.example.enterprise: DEBUG org.springframework.web: INFO pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
七、 完整项目启动与验证
7.1 启动步骤
- 1.创建数据库
enterprise_db - 2.执行表结构SQL
- 3.修改
application.yml中的数据库配置 - 4.运行
EnterpriseWebappApplication.main() - 5.访问
http://localhost:8080/api/v1/users
7.2 接口测试清单
- ✅ POST
/users- 创建用户(带参数校验) - ✅ GET
/users/{id}- 获取用户详情(异常处理) - ✅ GET
/users?page=1&size=10- 分页查询 - ✅ PUT
/users/{id}- 更新用户 - ✅ DELETE
/users/{id}- 逻辑删除
图5:完整接口测试截图(展示所有接口的请求和响应)
八、 总结与进阶方向
通过本文,你已经掌握了:
1.✅ 生产级项目结构设计
2.✅ SpringBoot 3 + MyBatis-Plus 3深度整合
3.✅ 分层架构(Entity/DTO/VO)实践
4.✅ 异常处理与统一响应
5.✅ 单元测试与质量保障
6.✅ 性能优化与最佳实践
下一步学习方向:
- 安全框架:集成Spring Security + JWT实现认证授权
- 缓存:整合Redis实现数据缓存
- 消息队列:集成RabbitMQ/Kafka处理异步任务
- 分布式:Spring Cloud微服务架构
- 部署:Docker容器化部署 + CI/CD流程
- 作者:小白原创声明:本文为CSDN原创技术博文,转载请注明出处
更多推荐



所有评论(0)