本文目录导读:

- 第一阶段:请求到达与参数接收(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)。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播