model分析

alt

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来保证代码的可维护性和类型安全。

日常学习 文章被收录于专栏

记录日常学习

全部评论

相关推荐

最近拿到了正浩的提前批offer感觉自己的实力得到了肯定,也给了我更多底气
搞机墨镜猫:正浩提前批官网好像就只有电力电子软硬件,哥们投的是这两个岗位吗
26届校招投递进展
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务