Spring Boot REST API项目如何手动实现DTO到Entity转换
实现思路确认
你选择手动编写Mapper类完成DTO与Entity转换、按Controller→Service→DAO分层流转数据的思路完全正确。这种实现方式是Spring Boot REST API开发的标准分层实践,手动实现映射逻辑不会引入额外依赖,还能帮你清晰理解各层职责边界、数据在链路中的流转规则,对初学者打基础的价值远高于直接使用第三方映射类库。
具体实现方案
分层职责约定
先明确各层的操作边界,避免逻辑混乱:
- Controller层:仅接收前端传入的请求DTO、返回响应DTO,绝对不直接操作数据库Entity,防止敏感字段(比如存储的加密密码、内部状态标记)直接暴露给前端
- Mapper层:仅负责DTO和Entity之间的字段映射转换,不编写任何业务逻辑、参数校验逻辑
- Service层:处理核心业务逻辑,调用DAO层前把入参DTO转换为Entity,返回结果前把DAO层查询到的Entity转换为响应DTO
- DAO/Repository层:仅和Entity打交道,负责数据库读写操作,不感知DTO的存在
代码实现示例
以最常见的用户管理接口为例,完整实现流程如下:
- 先定义数据库映射Entity
import jakarta.persistence.*; import lombok.Data; import java.time.LocalDateTime; @Data @Entity @Table(name = "sys_user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String encryptedPassword; private String email; private Integer age; private LocalDateTime createTime; private LocalDateTime updateTime; }
- 定义接口传输DTO
// 创建用户的请求入参DTO import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import lombok.Data; @Data public class UserCreateRequest { @NotBlank(message = "用户名不能为空") private String username; @NotBlank(message = "密码不能为空") private String rawPassword; @Email(message = "邮箱格式错误") private String email; private Integer age; }
// 用户信息返回给前端的响应DTO import lombok.Data; import java.time.LocalDateTime; @Data public class UserResponse { private Long id; private String username; private String email; private Integer age; private LocalDateTime createTime; }
- 手动编写Mapper转换类
把Mapper交给Spring容器管理为单例组件,分别写请求DTO转Entity、Entity转响应DTO的方法,记得做空值判断避免空指针:
import org.springframework.stereotype.Component; @Component public class UserMapper { // 创建请求转数据库实体 public User toEntity(UserCreateRequest request) { if (request == null) { return null; } User user = new User(); user.setUsername(request.getUsername()); // 注意:密码加密逻辑放在Service层做,Mapper只做字段映射 user.setEmail(request.getEmail()); user.setAge(request.getAge()); return user; } // 数据库实体转响应DTO public UserResponse toResponse(User entity) { if (entity == null) { return null; } UserResponse response = new UserResponse(); response.setId(entity.getId()); response.setUsername(entity.getUsername()); response.setEmail(entity.getEmail()); response.setAge(entity.getAge()); response.setCreateTime(entity.getCreateTime()); return response; } }
- Service层调用Mapper完成业务逻辑
import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @Service @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; private final UserMapper userMapper; private final PasswordEncoder passwordEncoder; public UserResponse createUser(UserCreateRequest request) { // 入参DTO转Entity User user = userMapper.toEntity(request); // 业务字段赋值 user.setEncryptedPassword(passwordEncoder.encode(request.getRawPassword())); user.setCreateTime(LocalDateTime.now()); user.setUpdateTime(LocalDateTime.now()); // 数据库落库 User savedUser = userRepository.save(user); // 保存结果转响应DTO返回 return userMapper.toResponse(savedUser); } }
- Controller层接收请求、返回结果
import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/v1/users") @RequiredArgsConstructor public class UserController { private final UserService userService; @PostMapping public ResponseEntity<UserResponse> createUser(@Valid @RequestBody UserCreateRequest request) { UserResponse response = userService.createUser(request); return ResponseEntity.status(HttpStatus.CREATED).body(response); } }
实现注意事项
- 转换方法必须先做空判断,避免链路中传入null值时触发空指针异常
- 字段名、字段类型不匹配的转换逻辑直接写在Mapper中即可,比如Entity中存的是Integer类型的性别编码,DTO中要返回中文的性别描述,直接在映射方法里做值转换
- 如果存在关联对象映射(比如User关联Role实体,DTO中需要返回Role的基础信息),直接在当前Mapper中注入对应关联对象的Mapper即可,不要手动new依赖类,交给Spring容器管理依赖
- 严格遵守单一职责:Mapper只做字段映射,参数校验、密码加密、时间赋值、权限判断这类逻辑全部放在Service层实现,不要耦合到Mapper中
内容的提问来源于stack exchange,提问作者Felipe
相关产品推荐
相关产品推荐

