本文目录导读:

- 第一阶段:请求到达与参数接收(Controller层)
- 第二阶段:参数校验(Controller / Service层)
- 第三阶段:DTO转Entity(Service层)
- 第四阶段:数据持久化(DAO/Repository层)
- 第五阶段:日志与审计(切面 / 全局)
- 第六阶段:统一异常处理与结果返回
- 完整流程图示
- 避坑指南
在Java项目(尤其是后端服务)中,规范的数据新增流程不仅仅是“写一个INSERT语句”,而是一套从请求接收、参数校验、业务逻辑、数据持久化、事务管理到结果返回的完整体系。
以下是一套标准的、可落地的Java数据新增流程规范(以Spring Boot + MyBatis/JPA为例):
第一阶段:请求到达与参数接收(Controller层)
规范目标: 解耦、清晰、统一入口。
- 使用DTO(Data Transfer Object)接收参数, 避免直接使用Entity对象接收前端请求。
- 原因: 前端传入的字段通常不等于数据库字段(如:前端传
confirmPassword,数据库没有;前端传createTime,应由后端自动生成)。
- 原因: 前端传入的字段通常不等于数据库字段(如:前端传
- 使用统一的Vo(View Object)/Result对象返回结果。
- 使用@Valid或@Validated进行基础校验。
@RestController
@RequestMapping("/api/users")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public Result<UserVO> addUser(@Valid @RequestBody UserAddDTO dto) {
log.info("新增用户请求: {}", dto);
UserVO userVO = userService.addUser(dto);
return Result.success(userVO);
}
}
第二阶段:参数校验(Controller / Service层)
规范目标: 分层校验,前端做格式校验,后端做安全与业务校验。
| 校验类型 | 位置 | 工具/注解 | 示例 |
|---|---|---|---|
| 基础格式校验 | Controller | @Valid + Jakarta Validation |
@NotBlank(message="姓名不能为空") |
| 业务唯一性校验 | Service | 自定义校验 / 数据库查询 | 用户名、邮箱、手机号是否已存在 |
| 敏感数据校验 | Service | 正则 / 白名单 | XSS过滤、SQL注入预防 |
| 关联数据校验 | Service | 数据库查询 | 外键ID是否存在(如部门ID) |
// 1. Controller层 - 基础校验
public class UserAddDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度在2-20之间")
private String username;
@NotBlank
@Email(message = "邮箱格式不正确")
private String email;
// getter/setter...
}
// 2. Service层 - 业务校验
public UserVO addUser(UserAddDTO dto) {
// 业务校验:检查用户名是否重复
if (userMapper.findByUsername(dto.getUsername()) != null) {
throw new BusinessException(ErrorCode.USERNAME_EXISTS);
}
// 业务校验:检查邮箱是否重复
if (userMapper.findByEmail(dto.getEmail()) != null) {
throw new BusinessException(ErrorCode.EMAIL_EXISTS);
}
// ... 其他业务逻辑
}
第三阶段:DTO转Entity(Service层)
规范目标: 数据转换清晰,保护实体类不被污染。
- 使用MapStruct、BeanUtils或手动set。
- 只拷贝业务需要的字段。
- 不信任前端传入的“敏感字段”: 如
isAdmin、roleId等,应由后端根据当前登录用户设置。
// 使用 MapStruct
@Mapper(componentModel = "spring")
public interface UserConvert {
User toEntity(UserAddDTO dto);
}
// Service
public UserVO addUser(UserAddDTO dto) {
// 转换
User user = userConvert.toEntity(dto);
// 补充后端字段
user.setStatus(1); // 默认启用
user.setCreateTime(LocalDateTime.now());
user.setPassword(passwordEncoder.encode(dto.getPassword())); // 加密
// 不信任前端设置的 isAdmin
user.setIsAdmin(false);
// ...
}
第四阶段:数据持久化(DAO/Repository层)
规范目标: 一次写入,关注幂等性与并发。
- 使用
@Transactional(事务)包裹新增操作。 如果需要写入多张表(如:用户表 + 用户角色表),必须保证原子性。 - 使用数据库自增ID或分布式ID(雪花算法)。
- 插入后“回读”: 如果需要返回ID或完整数据,可以显式查询一次或使用插入时的
useGeneratedKeys。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public UserVO addUser(UserAddDTO dto) {
// 1. 校验 ...
// 2. 转换 ...
User user = userConvert.toEntity(dto);
// 3. 插入用户表(返回自增ID)
userMapper.insert(user);
// user.getId() 已有值
// 4. 插入关联表(用户-角色)
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(dto.getRoleId());
userRoleMapper.insert(userRole);
// 5. 返回VO(必要时重新查询一次完整数据)
return userConvert.toVO(userMapper.findById(user.getId()));
}
}
第五阶段:日志与审计(切面 / 全局)
规范目标: 可追溯、可排查。
- 记录操作日志: “谁在什么时间新增了什么”。
- 使用Spring AOP
@Around+@AuditLog注解。 - 记录:操作人ID、IP地址、操作类型(CREATE)、目标对象ID、详细内容(非敏感)。
- 使用Spring AOP
- 自动填充审计字段:
create_by,create_time,update_by,update_time。- 方法1:使用MyBatis-Plus的
MetaObjectHandler。 - 方法2:使用JPA的
@CreatedDate/@LastModifiedDate+@EntityListeners。
- 方法1:使用MyBatis-Plus的
// MyBatis-Plus 自动填充
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "createBy", String.class, SecurityUtil.getCurrentUser());
}
}
第六阶段:统一异常处理与结果返回
规范目标: 不把数据库或框架的异常直接抛给前端。
- 全局异常拦截
@RestControllerAdvice。 - 分类处理:
ConstraintViolationException-> 返回参数校验错误。DuplicateKeyException-> 返回“数据已存在”。DataIntegrityViolationException-> 返回外键约束错误。BusinessException(自定义)-> 返回业务校验错误。
- 返回统一格式:
Result<T>(code + message + data)。
完整流程图示
客户端请求 (JSON)
↓
Controller 层 @Valid 格式校验
↓
Service 层 ① 业务校验(唯一性/关联性)
② DTO → Entity 转换
③ 补充后端字段(创建时间/创建人)
④ @Transactional 写入主表 + 子表
⑤ Entity → VO 转换
↓
DAO 层 insert (useGeneratedKeys)
↓
AOP 层 记录操作日志 / 填充审计字段
↓
全局异常处理 捕获各种异常,转化为统一Result
↓
Controller 返回 Result.success(data)
避坑指南
- 不要直接暴露Entity: 永远不要让前端直接传给Entity,否则可能出现“权限绕过”或“字段覆盖”。
- 不要手动拼接SQL: 使用MyBatis、JPA等ORM框架,防止SQL注入。
- 注意并发唯一性: 业务上的唯一(如用户名),除了Service层判断外,数据库要建
UNIQUE索引做兜底。 - 大字段处理: 如果新增时包含大文本(富文本、Markdown),考虑压缩存储或存OSS(对象存储)后只保存URL。
- 明确是“新增”还是“保存”: 如果是“新增”,
id应为null(或0);如果是“保存(新增/更新)”,需在Service中明确判断id是否存在。
这套规范的核心思想是:层层校验、职责分离、事务保证、可追溯性,适用于大部分企业级Java后端(Spring Boot + MyBatis/JPA)。