Java数据新增流程如何规范

wen java案例 30

本文目录导读:

Java数据新增流程如何规范

  1. 第一阶段:请求到达与参数接收(Controller层)
  2. 第二阶段:参数校验(Controller / Service层)
  3. 第三阶段:DTO转Entity(Service层)
  4. 第四阶段:数据持久化(DAO/Repository层)
  5. 第五阶段:日志与审计(切面 / 全局)
  6. 第六阶段:统一异常处理与结果返回
  7. 完整流程图示
  8. 避坑指南

在Java项目(尤其是后端服务)中,规范的数据新增流程不仅仅是“写一个INSERT语句”,而是一套从请求接收、参数校验、业务逻辑、数据持久化、事务管理到结果返回的完整体系。

以下是一套标准的、可落地的Java数据新增流程规范(以Spring Boot + MyBatis/JPA为例):

第一阶段:请求到达与参数接收(Controller层)

规范目标: 解耦、清晰、统一入口。

  1. 使用DTO(Data Transfer Object)接收参数, 避免直接使用Entity对象接收前端请求。
    • 原因: 前端传入的字段通常不等于数据库字段(如:前端传confirmPassword,数据库没有;前端传createTime,应由后端自动生成)。
  2. 使用统一的Vo(View Object)/Result对象返回结果。
  3. 使用@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层)

规范目标: 数据转换清晰,保护实体类不被污染。

  1. 使用MapStruct、BeanUtils或手动set。
  2. 只拷贝业务需要的字段。
  3. 不信任前端传入的“敏感字段”:isAdminroleId等,应由后端根据当前登录用户设置。
// 使用 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层)

规范目标: 一次写入,关注幂等性与并发。

  1. 使用@Transactional(事务)包裹新增操作。 如果需要写入多张表(如:用户表 + 用户角色表),必须保证原子性。
  2. 使用数据库自增ID或分布式ID(雪花算法)。
  3. 插入后“回读”: 如果需要返回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()));
    }
}

第五阶段:日志与审计(切面 / 全局)

规范目标: 可追溯、可排查。

  1. 记录操作日志: “谁在什么时间新增了什么”。
    • 使用Spring AOP @Around + @AuditLog注解。
    • 记录:操作人ID、IP地址、操作类型(CREATE)、目标对象ID、详细内容(非敏感)。
  2. 自动填充审计字段: create_by, create_time, update_by, update_time
    • 方法1:使用MyBatis-Plus的MetaObjectHandler
    • 方法2:使用JPA的@CreatedDate / @LastModifiedDate + @EntityListeners
// 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());
    }
}

第六阶段:统一异常处理与结果返回

规范目标: 不把数据库或框架的异常直接抛给前端。

  1. 全局异常拦截 @RestControllerAdvice
  2. 分类处理:
    • ConstraintViolationException -> 返回参数校验错误。
    • DuplicateKeyException -> 返回“数据已存在”。
    • DataIntegrityViolationException -> 返回外键约束错误。
    • BusinessException(自定义)-> 返回业务校验错误。
  3. 返回统一格式: Result<T>(code + message + data)。

完整流程图示

客户端请求 (JSON)
    ↓
Controller 层  @Valid 格式校验
    ↓
Service 层  ① 业务校验(唯一性/关联性)
               ② DTO → Entity 转换
               ③ 补充后端字段(创建时间/创建人)
               ④ @Transactional 写入主表 + 子表
               ⑤ Entity → VO 转换
    ↓
DAO 层      insert (useGeneratedKeys)
    ↓
AOP 层      记录操作日志 / 填充审计字段
    ↓
全局异常处理 捕获各种异常,转化为统一Result
    ↓
Controller 返回 Result.success(data)

避坑指南

  1. 不要直接暴露Entity: 永远不要让前端直接传给Entity,否则可能出现“权限绕过”或“字段覆盖”。
  2. 不要手动拼接SQL: 使用MyBatis、JPA等ORM框架,防止SQL注入。
  3. 注意并发唯一性: 业务上的唯一(如用户名),除了Service层判断外,数据库要建UNIQUE索引做兜底。
  4. 大字段处理: 如果新增时包含大文本(富文本、Markdown),考虑压缩存储或存OSS(对象存储)后只保存URL。
  5. 明确是“新增”还是“保存”: 如果是“新增”,id应为null(或0);如果是“保存(新增/更新)”,需在Service中明确判断id是否存在。

这套规范的核心思想是:层层校验、职责分离、事务保证、可追溯性,适用于大部分企业级Java后端(Spring Boot + MyBatis/JPA)。

抱歉,评论功能暂时关闭!