model分析
DTO 数据传输对象分类说明
完整分类表格
类型 | 层级 | 用途描述 | 校验规则 | 数据流向 | 示例场景 |
---|---|---|---|---|---|
Entity | 数据持久层 | 与数据库表严格对应,用于ORM操作 | 仅符合数据库约束 | 数据库 ↔ 服务层 | UserEntity 映射用户表 |
DTO | 数据传输抽象层 | 数据传输的通用抽象概念(可细分为以下子类) | 依赖具体实现 | 前后端/服务间通信 | 父类概念 |
Param | 请求入参层 | 接收前端创建/更新操作的提交数据 | 强校验:非空、格式、业务规则 | 前端 → 后端 | 用户注册表单提交 |
Query | 查询参数层 | 接收前端查询条件和分页参数 | 弱校验:允许空值,防攻击参数 | 前端 → 后端 | 用户列表分页查询 |
VO | 响应展示层 | 返回给前端的结构化数据(可能脱敏或聚合多个数据源) | 通常无校验 | 后端 → 前端 | 用户详情页数据展示 |
BO | Business Object | 业务逻辑层 | 包含业务规则和领域逻辑,可能组合多个Entity | 业务规则校验 | 订单价格计算 |
核心差异说明
1. Entity
- 必须与数据库表结构严格一致
- 禁止直接暴露给前端
- 示例字段:
@Entity
public class UserEntity {
@Id
private Long id;
private String password; // 敏感字段
}
2. Param
public class UserCreateParam {
@NotBlank(message = "用户名必填")
@Size(min = 4, max = 20)
private String username;
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d).{8,}$") // 密码复杂度
private String password;
}
3. Query
public class UserQuery {
private String keyword; // 可空
@Min @Max
private Integer pageSize = 10; // 默认值+防攻击
}
4. VO
public class UserVO {
private Long id;
private String username;
private String roleName; // 关联表字段转换
// 状态码转文字说明
public String getStatusText() {
return status == 1 ? "正常" : "冻结";
}
}
5. BO
public class OrderBO {
private List<OrderItem> items;
public BigDecimal calculateTotal() {
return items.stream()
.map(i -> i.getPrice().multiply(i.getQuantity()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
6.dto
// UserDTO.java - 服务层之间传递的业务对象
@Data
public class UserDTO {
private Long userId;
private String name; // 与Entity字段名不同
private Integer userStatus;
private String createTime; // 格式化后的字符串
}
工具推荐:
使用MapStruct进行DTO/Entity转换
使用Hibernate Validator进行参数校验
安全规范:
VO层必须过滤敏感字段(如password、token)
Query层必须限制分页最大值(防止全表扫描)
推荐 MyBatis 的 resultType 绑定到 Entity,然后在 Service 层转换为 VO/DTO 返回给上层
示例:
// UserEntity.java - 与数据库表结构对应
@Data
public class UserEntity {
private Long id;
private String username; // 数据库字段名
private String password;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
// UserDTO.java - 服务层之间传递的业务对象
@Data
public class UserDTO {
private Long userId;
private String name; // 与Entity字段名不同
private Integer userStatus;
private String createTime; // 格式化后的字符串
}
// UserVO.java - 返回给前端的精简数据
@Data
public class UserVO {
private Long id;
private String nickname; // 最终展示的字段名
private String registerTime;
}
MapStruct
// UserConvert.java
@Mapper(componentModel = "spring")
public interface UserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
@Mapping(source = "username", target = "name")
@Mapping(source = "status", target = "userStatus")
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
UserDTO toDTO(UserEntity entity);
@Mapping(source = "username", target = "nickname")
@Mapping(target = "registerTime", dateFormat = "yyyy-MM-dd")
UserVO toVO(UserEntity entity);
List<UserVO> toVOList(List<UserEntity> entities);
}
UserMapper.xml
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- 直接绑定到 Entity -->
<select id="selectById" resultType="com.example.entity.UserEntity">
SELECT * FROM user WHERE id = #{id}
</select>
<!-- 复杂查询也返回 Entity -->
<select id="selectByCondition" resultType="com.example.entity.UserEntity">
SELECT * FROM user
WHERE status = #{status}
<if test="username != null">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
</select>
</mapper>
UserService
// UserService.java
@Service
@RequiredArgsConstructor
public class UserService {
private final UserMapper userMapper;
private final UserConvert userConvert;
// 1. 查询返回DTO
public UserDTO getUserDTO(Long id) {
UserEntity entity = userMapper.selectById(id);
return userConvert.toDTO(entity);
}
// 2. 查询返回VO
public UserVO getUserVO(Long id) {
UserEntity entity = userMapper.selectById(id);
return userConvert.toVO(entity);
}
// 3. 复杂查询示例
public PageInfo<UserVO> queryUsers(UserQuery query) {
PageHelper.startPage(query.getPageNum(), query.getPageSize());
List<UserEntity> entities = userMapper.selectByCondition(query);
return PageInfo.of(userConvert.toVOList(entities));
}
}
UserController
// UserController.java
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public Result<UserVO> getUser(@PathVariable Long id) {
return Result.success(userService.getUserVO(id));
}
@GetMapping("/complex")
public Result<PageInfo<UserVO>> queryUsers(UserQuery query) {
return Result.success(userService.queryUsers(query));
}
}
MyBatis支持使用Map作为返回类型,但在生产环境中应谨慎使用,特别是对于核心业务逻辑,推荐使用明确的实体类或DTO来保证代码的可维护性和类型安全。
日常学习 文章被收录于专栏
记录日常学习