name: "springboot-mybatis-plus-standards"
description: "SpringBoot + MyBatis-Plus 后端开发规范,约束大模型生成符合团队标准的 Java 后端代码。当用户要求编写 SpringBoot 后端代码、生成 CRUD 接口、创建实体/Service/Controller 或任何涉及后端开发任务时必须遵循此规范。"
SpringBoot + MyBatis-Plus 后端开发规范
本规范用于约束所有由大模型生成的 SpringBoot + MyBatis-Plus 后端代码,确保代码风格统一、结构清晰、易于维护。
一、项目结构规范
com.example.project
├── common/ # 公共模块
│ ├── config/ # 全局配置类
│ ├── exception/ # 全局异常处理
│ ├── result/ # 统一响应体
│ └── utils/ # 工具类
├── module/ # 业务模块(按领域拆分)
│ ├── user/ # 用户模块
│ │ ├── controller/ # 控制器
│ │ ├── service/ # 服务接口
│ │ │ └── impl/ # 服务实现
│ │ ├── mapper/ # 数据访问层
│ │ ├── entity/ # 数据库实体(PO)
│ │ └── model/ # DTO/VO/Query
│ └── order/ # 订单模块(同上结构)
└── Application.java # 启动类
强制规则:
- 必须按业务模块分包,禁止按层分包(如所有 controller 放一个包)。
- 每个模块内部结构:
controller/service+impl/mapper/entity/model。 - 公共代码放在
common包下,不允许跨模块直接引用 entity。 - 启动类必须放在根包下,使用
@SpringBootApplication。
二、命名规范
| 类型 | 命名规则 | 示例 |
|---|---|---|
| 实体类(PO) | XxxEntity 或 Xxx |
UserEntity / User |
| 数据传输对象 | XxxDTO |
UserSaveDTO、UserUpdateDTO |
| 视图对象 | XxxVO |
UserVO、UserDetailVO |
| 查询对象 | XxxQuery 或 XxxPageQuery |
UserPageQuery |
| Mapper 接口 | XxxMapper |
UserMapper |
| Service 接口 | XxxService |
UserService |
| Service 实现 | XxxServiceImpl |
UserServiceImpl |
| Controller | XxxController |
UserController |
| 配置类 | XxxConfig |
MyBatisPlusConfig |
| 工具类 | XxxUtil 或 XxxUtils |
DateUtil |
| 枚举类 | XxxEnum |
UserStatusEnum |
方法命名:
- Controller 方法名与操作语义一致:
list()/detail()/save()/update()/delete() - Service 方法名与业务语义一致,禁止使用
addData()/doSomething()等模糊命名 - Mapper 自定义方法名遵循 MyBatis-Plus 风格:
selectXxxByCondition()/updateXxxById() - 布尔查询方法以
is/has/exists开头
三、实体类规范(Entity)
java
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_user") // 必须用 @TableName
public class UserEntity {
@TableId(type = IdType.AUTO) // 主键必须明确策略
private Long id;
/** 用户名 */
private String username;
/** 密码(不返回给前端) */
@TableField(select = false) // 敏感字段查询时不返回
private String password;
/** 逻辑删除标记(0-未删除 1-已删除) */
@TableLogic // 逻辑删除必须加
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
强制规则:
- 使用 Lombok
@Data,禁止手写 getter/setter。 - 表名必须通过
@TableName显式指定,表名格式为t_xxx(如t_user)。 - 主键使用
@TableId显式声明主键策略:IdType.AUTO(自增)或IdType.ASSIGN_ID(雪花)。 - 数据库必须存在
create_time、update_time字段 ,实体中对应LocalDateTime,配合自动填充。 - 涉及删除操作必须使用逻辑删除 ,字段
deleted(0=未删除,1=已删除),加@TableLogic。 - 密码等敏感字段使用
@TableField(select = false),防止查询时泄露。 - 禁止在 Entity 中使用
@JsonFormat等序列化注解,那是 VO 的职责。
四、DTO / VO / Query 规范
DTO(数据传输对象):仅用于接收请求参数
java
@Data
public class UserSaveDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度3-20位")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度6-20位")
private String password;
}
VO(视图对象):仅用于返回数据给前端
java
@Data
public class UserVO {
private Long id;
private String username;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}
Query(查询对象):用于分页/条件查询
java
@Data
@EqualsAndHashCode(callSuper = true)
public class UserPageQuery extends PageQuery {
/** 用户名模糊搜索 */
private String username;
/** 状态筛选 */
private Integer status;
}
强制规则:
- DTO 参数校验使用 Jakarta Validation 注解(
@NotBlank、@NotNull、@Size等),message 必须明确。 - Controller 接收参数统一使用 DTO,禁止直接使用 Entity 接收请求参数。
- Controller 返回数据统一使用 VO,禁止直接返回 Entity 给前端。
- 分页查询对象统一继承
PageQuery基类(见下文)。 - DTO/VO 之间的转换使用 MapStruct 或在 Service 层手动赋值,禁止在 Controller 调用 Entity 的 setter。
五、Mapper 层规范
java
@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
// MyBatis-Plus 的 BaseMapper 提供基础 CRUD,一般无需写 XML
/**
* 自定义分页查询(多表关联时使用)
*/
IPage<UserVO> selectPageByCondition(Page<UserEntity> page, @Param("query") UserPageQuery query);
}
强制规则:
- Mapper 接口必须标注
@Mapper,继承BaseMapper<Entity>。 - 简单 CRUD 直接使用 MyBatis-Plus 内置方法,禁止重复定义。
- 仅在多表关联、复杂聚合查询时编写自定义 SQL,SQL 写在 XML 或注解中。
- 分页查询返回
IPage<VO>而非IPage<Entity>,直接用 VO 接收结果。 - 禁止在 Mapper 层做业务逻辑处理,仅负责数据访问。
- 禁止使用
${}拼接 SQL 变量,一律使用#{}防止 SQL 注入。
六、Service 层规范
接口:
java
public interface UserService extends IService<UserEntity> {
IPage<UserVO> pageUser(UserPageQuery query);
void saveUser(UserSaveDTO dto);
void updateUser(UserUpdateDTO dto);
}
实现:
java
@Service
@RequiredArgsConstructor // 构造器注入
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {
private final UserMapper userMapper;
@Override
public IPage<UserVO> pageUser(UserPageQuery query) {
Page<UserEntity> page = query.toPage();
return userMapper.selectPageByCondition(page, query);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveUser(UserSaveDTO dto) {
// 1. 业务校验
if (lambdaQuery().eq(UserEntity::getUsername, dto.getUsername()).exists()) {
throw new BusinessException("用户名已存在");
}
// 2. DTO → Entity
UserEntity user = new UserEntity();
user.setUsername(dto.getUsername());
user.setPassword(BCrypt.hashpw(dto.getPassword(), BCrypt.gensalt()));
// 3. 入库
save(user);
}
}
强制规则:
- Service 接口继承
IService<Entity>,实现类继承ServiceImpl<Mapper, Entity>。 - 使用
@RequiredArgsConstructor+final字段实现构造器注入,禁止使用@Autowired字段注入。 - 增删改操作必须加
@Transactional(rollbackFor = Exception.class)。 - 业务校验在 Service 层完成,Controller 仅负责参数校验和路由。
- 条件查询优先使用
LambdaQueryWrapper,禁止字符串硬编码字段名:
java
// 正确
lambdaQuery().eq(UserEntity::getUsername, username).list()
// 错误
new QueryWrapper<UserEntity>().eq("username", username).list()
- 批量操作使用 MyBatis-Plus 的
saveBatch()/updateBatchById(),设置合理batchSize(建议 500)。 - Entity 之间的转换在 Service 层进行,禁止将 Entity 传递到 Controller 层。
七、Controller 层规范
全部接口统一使用 POST 请求,仅文件下载等流式响应接口可使用 GET。
java
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Validated
public class UserController {
private final UserService userService;
@PostMapping("/list")
public Result<IPage<UserVO>> list(@Valid @RequestBody UserPageQuery query) {
return Result.success(userService.pageUser(query));
}
@PostMapping("/detail")
public Result<UserVO> getById(@Valid @RequestBody IdDTO dto) {
return Result.success(userService.getByUserId(dto.getId()));
}
@PostMapping("/save")
public Result<Void> save(@Valid @RequestBody UserSaveDTO dto) {
userService.saveUser(dto);
return Result.success();
}
@PostMapping("/update")
public Result<Void> update(@Valid @RequestBody UserUpdateDTO dto) {
userService.updateUser(dto.getId(), dto);
return Result.success();
}
@PostMapping("/delete")
public Result<Void> delete(@Valid @RequestBody IdDTO dto) {
userService.deleteUser(dto.getId());
return Result.success();
}
}
IdDTO 公共类(用于仅需传 ID 的接口):
java
@Data
public class IdDTO {
@NotNull(message = "ID不能为空")
private Long id;
}
强制规则:
- 所有接口统一使用
@PostMapping,禁止使用@GetMapping/@PutMapping/@DeleteMapping。 - 文件下载、导出 Excel 等流式响应接口例外,可使用
@GetMapping。 - 使用
@RestController,禁止使用@Controller+@ResponseBody。 - 路径统一
/api/{module}s/{action},module 用复数形式,action 明确描述操作。 - 所有返回值必须用
Result<T>包裹,禁止直接返回实体或 Map。 - 接收参数必须
@Valid+@RequestBody校验,禁止使用@PathVariable传递业务参数。 - Controller 类上加
@Validated。 - Controller 层禁止写业务逻辑,仅做参数接收、校验、调用 Service、返回结果。
- 使用
@RequiredArgsConstructor构造器注入。 - 接口路径规范:
- 分页列表:
@PostMapping("/list") - 查询详情:
@PostMapping("/detail") - 新增:
@PostMapping("/save") - 修改:
@PostMapping("/update") - 删除:
@PostMapping("/delete")
- 分页列表:
八、统一响应体 Result<T>
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success() {
return new Result<>(200, "操作成功", null);
}
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
public static <T> Result<T> error(String message) {
return new Result<>(500, message, null);
}
}
- 所有接口返回
Result<T>,HTTP 状态码始终 200,业务状态码在 body 中区分。 - 成功统一返回 code=200,业务异常通过
BusinessException+ 全局异常处理器处理。 - 禁止在 Controller 中
try-catch包装返回;异常统一由@RestControllerAdvice处理。
九、全局异常处理
java
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/** 参数校验异常 */
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValid(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining("; "));
return Result.error(400, msg);
}
/** 业务异常 */
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusiness(BusinessException e) {
log.warn("业务异常: {}", e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
/** 兜底异常 */
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.error(500, "服务器内部错误");
}
}
java
// BusinessException
public class BusinessException extends RuntimeException {
private Integer code = 500;
public BusinessException(String message) {
super(message);
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public Integer getCode() { return code; }
}
十、分页查询规范
java
@Data
public class PageQuery {
@Min(value = 1, message = "页码最小为1")
private Integer pageNum = 1;
@Min(value = 1, message = "每页至少1条")
@Max(value = 100, message = "每页最多100条")
private Integer pageSize = 10;
/** 转为 MyBatis-Plus Page 对象,关闭 count 总数时调用 toPage(false) */
public <T> Page<T> toPage() {
return new Page<>(pageNum, pageSize);
}
public <T> Page<T> toPage(boolean searchCount) {
return new Page<>(pageNum, pageSize, searchCount);
}
}
强制规则:
- 所有分页查询参数类必须继承
PageQuery。 - 分页参数默认值:
pageNum=1,pageSize=10。 - 必须限制
pageSize上限(≤100),防止全量导出拖垮数据库。 - SQL 中使用
ORDER BY create_time DESC确保分页数据一致性。
十一、MyBatis-Plus 配置
java
@Configuration
public class MyBatisPlusConfig {
/** 分页插件 */
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
/** 自动填充创建时间/更新时间 */
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
};
}
}
强制规则:
- 必须配置分页插件
PaginationInnerInterceptor,否则分页不生效。 - 必须配置
MetaObjectHandler自动填充createTime和updateTime。 - 逻辑删除配置在
application.yml中:
yaml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1 # 已删除
logic-not-delete-value: 0 # 未删除
十二、通用禁止项
- 禁止字段注入 :不允许
@Autowired字段注入,统一构造器注入。 - 禁止魔法值 :状态、类型等固定值必须用枚举类,禁止代码中直接写
1/2/"A"/"B"。 - 禁止 Entity 直连 Controller:Entity → DTO/VO 转换在 Service 层完成。
- 禁止在循环中操作数据库 :批量场景使用 MyBatis-Plus
saveBatch/updateBatchById。 - 禁止 try-catch 吞异常 :异常要么向上抛,要么记录日志后抛
BusinessException。 - 禁止 SQL 字符串拼接 :防止 SQL 注入,占位符只用
#{}。 - 禁止
SELECT *:自定义 SQL 必须明确列出需要的字段。 - 禁止
@RequestMapping不指定 method :必须使用@GetMapping/@PostMapping等明确 HTTP 方法。
十三、依赖版本参考
xml
<!-- Spring Boot 3.x + JDK 17+ -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
</parent>
<properties>
<java.version>17</java.version>
<mybatis-plus.version>3.5.7</mybatis-plus.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
</properties>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
十四、代码生成速查
| 场景 | 正确写法 |
|---|---|
| 条件查询 | lambdaQuery().eq(Entity::getField, value).list() |
| 分页查询 | baseMapper.selectPage(page, wrapper) |
| 单一条件更新 | lambdaUpdate().eq(Entity::getId, id).set(Entity::getField, value).update() |
| 批量插入 | saveBatch(list, 500) |
| 分组聚合 | 自定义 Mapper + XML |
| 存在性检查 | lambdaQuery().eq(Entity::getField, value).exists() |
| 计数 | lambdaQuery().eq(Entity::getField, value).count() |
生成代码时必须逐条对照本规范检查,不符合的代码视为不合格。