Entity、VO、DTO、Form 对象详解

概述

在项目中,我们使用不同的对象模型 来处理不同场景的数据,这是分层架构的重要体现。

为什么需要多种对象?

  • 🔐 安全性:防止敏感数据泄露
  • 🎯 职责分离:每个对象只关注自己的职责
  • 🔄 灵活性:不同层可以独立演化
  • 🛡️ 解耦:数据库变动不影响前端

1️⃣ Entity(实体类)- 数据库映射

定义

Entity 是与数据库表 一一对应的 Java 对象,也叫持久化对象

特点:

  • 🗄️ 对应数据库表结构
  • 📦 包含所有字段(包括敏感字段)
  • 🔗 包含数据库注解(如 @Table、@Column)
  • 💾 只在 DAO/Mapper 层使用

实战案例 - User 实体

java 复制代码
package com.MiniBlog.entity;

import lombok.Data;
import javax.persistence.*;
import java.util.Date;

/**
 * 用户实体类 - 对应数据库表 tb_user
 * 
 * 【注解说明】
 * @Entity:JPA注解,表示这是一个实体类
 * @Table:指定对应的数据库表名
 * @Data:Lombok注解,自动生成getter/setter/toString等
 */
@Data
@Entity
@Table(name = "tb_user")
public class User {
    
    // ========== 【主键】 ==========
    /**
     * @Id:主键标识
     * @GeneratedValue:主键生成策略(自增)
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    // ========== 【基本信息】 ==========
    
    /**
     * 用户编号(业务主键)
     */
    @Column(name = "no", length = 50)
    private String no;
    
    /**
     * 真实姓名
     */
    @Column(name = "realname", length = 100)
    private String realname;
    
    /**
     * 手机号
     */
    @Column(name = "mobile", length = 20)
    private String mobile;
    
    /**
     * 邮箱
     */
    @Column(name = "email", length = 100)
    private String email;
    
    // ========== 【敏感信息】⚠️ ==========
    
    /**
     * 密码(加密后的)
     * 【注意】这个字段不应该返回给前端!
     */
    @Column(name = "password", length = 200)
    private String password;
    
    /**
     * 密码盐值
     * 【注意】这个字段不应该返回给前端!
     */
    @Column(name = "salt", length = 50)
    private String salt;
    
    /**
     * 身份证号
     * 【注意】需要脱敏后才能返回给前端!
     */
    @Column(name = "cardno", length = 50)
    private String cardno;
    
    // ========== 【状态字段】 ==========
    
    /**
     * 账号状态:1-正常,2-冻结,3-注销
     */
    @Column(name = "status")
    private Integer status;
    
    /**
     * 是否删除:0-否,1-是
     */
    @Column(name = "deleted")
    private Integer deleted;
    
    // ========== 【微信相关】 ==========
    
    @Column(name = "openid", length = 100)
    private String openid;
    
    @Column(name = "unionid", length = 100)
    private String unionid;
    
    @Column(name = "mp_openid", length = 100)
    private String mpOpenid;
    
    // ========== 【人脸识别】 ==========
    
    @Column(name = "faceid", length = 100)
    private String faceid;
    
    // ========== 【时间戳】 ==========
    
    /**
     * 创建时间
     */
    @Column(name = "create_time")
    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;
    
    /**
     * 更新时间
     */
    @Column(name = "update_time")
    @Temporal(TemporalType.TIMESTAMP)
    private Date updateTime;
    
    /**
     * 创建人
     */
    @Column(name = "create_user")
    private Integer createUser;
    
    /**
     * 更新人
     */
    @Column(name = "update_user")
    private Integer updateUser;
}

数据库表结构(对应):

sql 复制代码
CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `no` varchar(50) DEFAULT NULL COMMENT '用户编号',
  `realname` varchar(100) DEFAULT NULL COMMENT '真实姓名',
  `mobile` varchar(20) DEFAULT NULL COMMENT '手机号',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `password` varchar(200) DEFAULT NULL COMMENT '密码',
  `salt` varchar(50) DEFAULT NULL COMMENT '盐值',
  `cardno` varchar(50) DEFAULT NULL COMMENT '身份证号',
  `status` int(11) DEFAULT '1' COMMENT '状态',
  `deleted` int(11) DEFAULT '0' COMMENT '是否删除',
  `openid` varchar(100) DEFAULT NULL COMMENT '微信openid',
  `unionid` varchar(100) DEFAULT NULL COMMENT '微信unionid',
  `mp_openid` varchar(100) DEFAULT NULL COMMENT '公众号openid',
  `faceid` varchar(100) DEFAULT NULL COMMENT '人脸ID',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `create_user` int(11) DEFAULT NULL COMMENT '创建人',
  `update_user` int(11) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_mobile` (`mobile`),
  KEY `idx_no` (`no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

2️⃣ VO(View Object)- 视图对象

定义

VO返回给前端的视图对象,只包含前端需要展示的数据。

特点:

  • 👁️ 只包含前端需要的字段
  • 🔒 不包含敏感字段(密码、盐值等)
  • 🎨 可能包含计算字段(如年龄、格式化日期)
  • 📤 只在 Controller 层返回给前端

实战案例 - UserVo

java 复制代码
package com.MiniBlog.vo.user;

import lombok.Data;
import com.payslip.entity.UserCard;
import java.util.List;

/**
 * 用户视图对象 - 返回给前端
 * 
 * 【设计原则】
 * 1. 只包含前端需要的字段
 * 2. 敏感字段不包含(如password、salt)
 * 3. 需要脱敏的字段已处理(如cardno)
 * 4. 可以包含关联对象(如银行卡列表)
 */
@Data
public class UserVo {
    
    // ========== 【基本信息】 ==========
    
    private Integer id;
    
    private String no;
    
    private String realname;
    
    /**
     * 手机号(脱敏)
     * 例如:138****5678
     */
    private String mobile;
    
    /**
     * 邮箱(可能脱敏)
     * 例如:abc***@qq.com
     */
    private String email;
    
    /**
     * 身份证号(脱敏)
     * 例如:320***********1234
     */
    private String cardno;
    
    // ========== 【注意】❌ 不包含这些字段 ==========
    // private String password;  // 密码不返回
    // private String salt;      // 盐值不返回
    
    // ========== 【状态】 ==========
    
    private Integer status;
    
    /**
     * 状态文本(前端显示用)
     * 计算属性,根据 status 值生成
     */
    private String statusText;
    
    // ========== 【头像】 ==========
    
    private String avatar;
    
    // ========== 【微信信息】 ==========
    
    /**
     * 是否绑定微信
     * 计算属性:openid 不为空则已绑定
     */
    private Boolean hasWechat;
    
    /**
     * 是否绑定公众号
     * 计算属性:mpOpenid 不为空则已绑定
     */
    private Boolean hasMpWechat;
    
    // ========== 【关联信息】 ==========
    
    /**
     * 用户的银行卡列表
     * 这是关联查询的结果,Entity 中没有这个字段
     */
    private List<UserCard> cards;
    
    // ========== 【时间】 ==========
    
    /**
     * 创建时间(格式化后)
     * 例如:2024-01-15 10:30:00
     */
    private String createTime;
    
    // ========== 【计算字段】 ==========
    
    /**
     * 账号年龄(天数)
     * 根据创建时间计算,Entity 中没有这个字段
     */
    private Integer accountAge;
    
    /**
     * 是否实名认证
     * 根据 cardno 是否为空判断
     */
    private Boolean isRealAuth;
}

3️⃣ DTO(Data Transfer Object)- 数据传输对象

定义

DTO服务间传输数据的对象,用于跨层或跨服务传递数据。

特点:

  • 📡 用于 Service 层之间传递数据
  • 🔄 用于微服务之间传递数据
  • 📦 可能包含多个实体的数据
  • 🎯 职责单一,只负责数据传输

实战案例 - UserTokenDTO

java 复制代码
package com.MiniBlog.dto.user;

import lombok.Data;
import lombok.Builder;

/**
 * 用户登录成功后返回的数据传输对象
 * 
 * 【使用场景】
 * - 用户注册成功
 * - 用户登录成功
 * - Token刷新成功
 */
@Data
@Builder
public class UserTokenDTO {
    
    /**
     * 用户ID
     */
    private Integer userId;
    
    /**
     * 用户编号
     */
    private String userNo;
    
    /**
     * 用户姓名
     */
    private String userName;
    
    /**
     * 访问令牌(JWT Token)
     * 前端需要保存,每次请求携带
     */
    private String token;
    
    /**
     * Token过期时间(秒)
     */
    private Long expiresIn;
    
    /**
     * 刷新令牌
     * 用于Token过期后刷新
     */
    private String refreshToken;
    
    /**
     * 是否首次登录
     * 首次登录需要引导用户完善信息
     */
    private Boolean firstLogin;
    
    /**
     * 用户头像
     */
    private String avatar;
}

4️⃣ Form(表单对象)- 接收前端数据

定义

Form接收前端提交数据的对象,包含参数校验规则。

特点:

  • 📥 只在 Controller 层接收前端数据
  • ✅ 包含参数校验注解(@NotNull、@Size等)
  • 🎯 一个接口一个Form,职责明确
  • 🔒 校验规则集中管理

实战案例 - UserMobileRegisterDTO

java 复制代码
package com.MiniBlog.form.user;

import lombok.Data;
import javax.validation.constraints.*;

/**
 * 用户手机号注册表单
 * 
 * 【使用场景】
 * POST /user/mobile-register
 * 
 * 【校验规则】
 * - 手机号必填,格式正确
 * - 验证码必填,6位数字
 * - 密码必填,6-20位
 */
@Data
public class UserMobileRegisterDTO {
    
    /**
     * 手机号
     * 
     * @NotBlank:不能为空(会自动trim)
     * @Pattern:正则校验
     */
    @NotBlank(message = "手机号不能为空")
    @Pattern(
        regexp = "^1[3-9]\\d{9}$", 
        message = "手机号格式不正确"
    )
    private String mobile;
    
    /**
     * 短信验证码
     */
    @NotBlank(message = "验证码不能为空")
    @Size(min = 4, max = 6, message = "验证码长度为4-6位")
    private String code;
    
    /**
     * 密码
     */
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度为6-20位")
    private String password;
    
    /**
     * 邀请码(可选)
     */
    private String inviteCode;
}

5️⃣ 对象之间的转换流程

完整数据流转图

sql 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                         前端(浏览器/APP)                         │
└────────────┬────────────────────────────────────────┬────────────┘
            │                                        │
            │ ① 发送请求                              │ ⑥ 接收响应
            │ JSON: {mobile, code, password}        │ JSON: {userId, token, userName}
            ▼                                        ▲
┌─────────────────────────────────────────────────────────────────┐
│                      Controller 层(控制器)                       │
│  @PostMapping("/register")                                      │
│  public ApiResponse<UserTokenDTO> register(                     │
│      @Valid @RequestBody UserMobileRegisterDTO form) {          │
│                                                                 │
│      ② Form 接收参数                  ⑤ VO 返回给前端              │
│      └─> 自动校验(@Valid)           └─> Entity 转 VO            │
│                                                                 │
│      UserTokenDTO dto = userService.register(form);            │
│      return ApiResponse.ok(dto);                               │
│  }                                                              │
└────────────┬────────────────────────────────────────┬────────────┘
            │                                        │
            │ ③ Form 传给 Service                    │ ④ DTO 返回
            ▼                                        ▲
┌─────────────────────────────────────────────────────────────────┐
│                       Service 层(业务逻辑)                       │
│  public UserTokenDTO register(UserMobileRegisterDTO form) {    │
│                                                                 │
│      // 1. 校验验证码                                            │
│      validateCode(form.getMobile(), form.getCode());           │
│                                                                 │
│      // 2. 创建 Entity 对象                                      │
│      User user = new User();                                   │
│      user.setMobile(form.getMobile());                         │
│      user.setPassword(encryptPassword(form.getPassword()));    │
│      user.setCreateTime(new Date());                           │
│                                                                 │
│      // 3. 保存到数据库                                          │
│      userDao.save(user);  ────┐                                │
│                               │                                │
│      // 4. 生成 Token                                           │
│      String token = generateToken(user.getId());               │
│                                                                 │
│      // 5. 构建 DTO 返回                                         │
│      return UserTokenDTO.builder()                             │
│          .userId(user.getId())                                 │
│          .token(token)                                         │
│          .userName(user.getRealname())                         │
│          .build();                                             │
│  }                            │                                │
└───────────────────────────────┼─────────────────────────────────┘
                                │
                                │ Entity 保存/查询
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                      DAO/Mapper 层(数据访问)                     │
│                                                                 │
│  public interface UserRepository {                             │
│      User save(User user);           // 保存                    │
│      User findById(Integer id);      // 查询                    │
│  }                                                              │
└────────────┬────────────────────────────────────────┬────────────┘
            │                                        │
            │ SQL 语句                               │ 查询结果
            ▼                                        ▲
┌─────────────────────────────────────────────────────────────────┐
│                         数据库(MySQL)                           │
│                                                                 │
│  tb_user 表                                                     │
│  ┌────┬──────┬──────────┬──────────┬──────────┐                │
│  │ id │  no  │  mobile  │ password │   salt   │                │
│  ├────┼──────┼──────────┼──────────┼──────────┤                │
│  │ 1  │ U001 │ 13800000 │ ******* │ ******* │                │
│  └────┴──────┴──────────┴──────────┴──────────┘                │
└─────────────────────────────────────────────────────────────────┘

6️⃣ 对象转换代码示例

实际项目中的转换

java 复制代码
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController extends BaseController {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private UserCardService userCardService;
    
    /**
     * 根据Token获取用户信息
     * 
     * 【数据流转】
     * 1. 从数据库查询 Entity(包含所有字段)
     * 2. Entity 转换为 VO(只包含安全字段)
     * 3. 关联查询银行卡(补充数据)
     * 4. 返回 VO 给前端
     */
    @GetMapping("/findByToken")
    public ApiResponse<UserVo> findByToken() {
        
        // ========== ① 查询 Entity ==========
        Integer userId = LoginContext.getUserId();
        User user = userService.findById(userId);  // Entity 对象
        
        // 校验用户存在
        Asserts.notNull(user, -10001, "登录失效");
        
        // ========== ② Entity 转 VO ==========
        // 使用 BeanUtil 复制属性(只复制同名字段)
        UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
        
        // ========== ③ 补充数据 ==========
        // 查询关联的银行卡列表
        List<UserCard> cards = userCardService.findByUserId(user.getId());
        userVo.setCards(cards);
        
        // 设置计算字段
        userVo.setHasWechat(user.getOpenid() != null);
        userVo.setIsRealAuth(user.getCardno() != null);
        
        // 脱敏处理
        if (user.getMobile() != null) {
            userVo.setMobile(desensitizeMobile(user.getMobile()));
        }
        if (user.getCardno() != null) {
            userVo.setCardno(desensitizeCardNo(user.getCardno()));
        }
        
        // ========== ④ 返回 VO ==========
        return ApiResponse.ok(userVo);
    }
    
    /**
     * 手机号脱敏
     * 13800138000 -> 138****8000
     */
    private String desensitizeMobile(String mobile) {
        if (mobile == null || mobile.length() != 11) {
            return mobile;
        }
        return mobile.substring(0, 3) + "****" + mobile.substring(7);
    }
    
    /**
     * 身份证号脱敏
     * 320102199001011234 -> 320***********1234
     */
    private String desensitizeCardNo(String cardNo) {
        if (cardNo == null || cardNo.length() < 8) {
            return cardNo;
        }
        return cardNo.substring(0, 3) + "***********" + cardNo.substring(cardNo.length() - 4);
    }
}

使用工具类转换(推荐)

java 复制代码
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;

/**
 * 对象转换工具类
 */
public class BeanConverter {
    
    /**
     * Entity 转 VO
     * 自动忽略 null 值
     */
    public static <T> T toVo(Object source, Class<T> targetClass) {
        if (source == null) {
            return null;
        }
        return BeanUtil.copyProperties(source, targetClass, 
            CopyOptions.create().ignoreNullValue());
    }
    
    /**
     * Entity List 转 VO List
     */
    public static <S, T> List<T> toVoList(List<S> sourceList, Class<T> targetClass) {
        if (sourceList == null || sourceList.isEmpty()) {
            return Collections.emptyList();
        }
        return sourceList.stream()
            .map(source -> toVo(source, targetClass))
            .collect(Collectors.toList());
    }
    
    /**
     * Form 转 Entity
     */
    public static <T> T toEntity(Object form, Class<T> entityClass) {
        if (form == null) {
            return null;
        }
        T entity = BeanUtil.copyProperties(form, entityClass);
        // 设置创建时间等默认字段
        if (entity instanceof BaseEntity) {
            ((BaseEntity) entity).setCreateTime(new Date());
        }
        return entity;
    }
}

// 使用示例
@Service
public class UserServiceImpl implements UserService {
    
    @Override
    public UserVo getUserInfo(Integer userId) {
        // 1. 查询 Entity
        User user = userDao.findById(userId);
        
        // 2. 转换为 VO
        UserVo vo = BeanConverter.toVo(user, UserVo.class);
        
        // 3. 补充额外数据
        vo.setCards(userCardService.findByUserId(userId));
        
        return vo;
    }
    
    @Override
    public List<UserVo> getUserList(List<Integer> userIds) {
        // 1. 批量查询 Entity
        List<User> users = userDao.findByIds(userIds);
        
        // 2. 批量转换为 VO
        return BeanConverter.toVoList(users, UserVo.class);
    }
}

7️⃣ 为什么要分这么多对象?

核心原因

1. 安全性
java 复制代码
// ❌ 错误:直接返回 Entity
@GetMapping("/user/{id}")
public ApiResponse<User> getUser(@PathVariable Integer id) {
    User user = userService.findById(id);
    return ApiResponse.success(user);  // 密码、盐值都返回了!
}

// 前端收到的数据(危险!)
{
    "id": 123,
    "mobile": "13800138000",
    "password": "e10adc3949ba59abbe56e057f20f883e",  // MD5密码
    "salt": "a1b2c3d4",                              // 盐值暴露
    "cardno": "320102199001011234"                   // 身份证明文
}

// ✅ 正确:返回 VO
@GetMapping("/user/{id}")
public ApiResponse<UserVo> getUser(@PathVariable Integer id) {
    User user = userService.findById(id);
    UserVo vo = BeanUtil.copyProperties(user, UserVo.class);
    // VO 中没有 password、salt 字段
    return ApiResponse.success(vo);
}

// 前端收到的数据(安全)
{
    "id": 123,
    "mobile": "138****8000",           // 脱敏
    "cardno": "320***********1234"     // 脱敏
}
2. 解耦
java 复制代码
// 数据库表结构变更,不影响前端

// 数据库改了字段名:user_name -> real_name
@Entity
public class User {
    @Column(name = "real_name")  // 数据库字段改了
    private String realname;      // Java字段不变
}

// VO 不变,前端不受影响
public class UserVo {
    private String realname;  // 前端继续用原来的字段
}
3. 灵活性
java 复制代码
// VO 可以包含 Entity 没有的计算字段
public class UserVo {
    private Integer id;
    private String realname;
    
    // ========== 【计算字段】 ==========
    // Entity 中没有这些字段
    
    private Integer age;           // 根据生日计算
    private String statusText;     // 根据status转换(1->正常,2->冻结)
    private Boolean isVip;         // 根据会员等级判断
    private String createTimeText; // 格式化日期(2024-01-15)
    
    // ========== 【关联数据】 ==========
    // Entity 中没有这些字段
    
    private List<UserCard> cards;      // 关联的银行卡
    private Integer orderCount;        // 订单数量(统计)
    private BigDecimal totalAmount;    // 总消费金额(统计)
}
4. 职责分离
java 复制代码
// 每个对象都有明确的职责

Entity:  负责与数据库交互
  ↓
DTO:    负责在Service层传递数据
  ↓
VO:     负责返回给前端展示

Form:   负责接收前端提交的数据
  ↓
DTO:    负责在Service层传递数据
  ↓
Entity: 负责保存到数据库

8️⃣ 实战完整流程示例

用户注册完整流程

java 复制代码
// ========== ① 前端提交表单 ==========
// POST /user/register
// Body: { "mobile": "13800138000", "code": "1234", "password": "abc123" }

// ========== ② Controller 接收 Form ==========
@RestController
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @PostMapping("/register")
    public ApiResponse<UserTokenDTO> register(
            @Valid @RequestBody UserMobileRegisterDTO form) {  // Form 对象
        
        log.info("用户注册: mobile={}", form.getMobile());
        
        // 调用 Service,传递 Form,接收 DTO
        UserTokenDTO dto = userService.register(form);
        
        // 返回 DTO 给前端
        return ApiResponse.ok(dto);
    }
}

// ========== ③ Service 处理业务逻辑 ==========
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserRepository userDao;
    
    @Autowired
    private SmsService smsService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserTokenDTO register(UserMobileRegisterDTO form) {
        
        // 1. 校验验证码
        boolean valid = smsService.validateCode(form.getMobile(), form.getCode());
        Asserts.isTrue(valid, "验证码错误");
        
        // 2. 检查手机号是否已注册
        User existUser = userDao.findByMobile(form.getMobile());
        Asserts.isNull(existUser, "手机号已注册");
        
        // 3. Form 转 Entity
        User user = new User();
        user.setNo(generateUserNo());                        // 生成用户编号
        user.setMobile(form.getMobile());                    // 手机号
        user.setRealname("用户" + user.getNo());             // 默认昵称
        
        // 4. 密码加密
        String salt = UUID.randomUUID().toString();
        String encryptedPwd = Secure.encryptPassword(form.getPassword(), salt);
        user.setSalt(salt);
        user.setPassword(encryptedPwd);
        
        // 5. 设置默认值
        user.setStatus(1);                    // 正常
        user.setDeleted(0);                   // 未删除
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        
        // 6. 保存到数据库(Entity)
        userDao.save(user);
        
        log.info("用户注册成功: userId={}", user.getId());
        
        // 7. 生成 Token
        String token = JwtUtil.generateToken(user.getId(), user.getNo());
        
        // 8. 构建 DTO 返回
        return UserTokenDTO.builder()
            .userId(user.getId())
            .userNo(user.getNo())
            .userName(user.getRealname())
            .token(token)
            .expiresIn(7200L)  // 2小时
            .firstLogin(true)
            .build();
    }
    
    private String generateUserNo() {
        return "U" + System.currentTimeMillis();
    }
}

// ========== ④ DAO 保存 Entity ==========
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    
    /**
     * 根据手机号查询用户
     */
    User findByMobile(String mobile);
    
    /**
     * 根据用户编号查询
     */
    User findByNo(String no);
}

// ========== ⑤ 数据库存储 ==========
// INSERT INTO tb_user 
// (no, mobile, realname, password, salt, status, deleted, create_time, update_time) 
// VALUES 
// ('U1704528000123', '13800138000', '用户U1704528000123', 
//  'e10adc3949ba59abbe56e057f20f883e', 'a1b2c3d4', 1, 0, NOW(), NOW());

// ========== ⑥ 返回给前端 ==========
// Response:
{
    "code": 0,
    "message": "成功",
    "data": {
        "userId": 123,
        "userNo": "U1704528000123",
        "userName": "用户U1704528000123",
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "expiresIn": 7200,
        "firstLogin": true
    }
}

9️⃣ 最佳实践与规范

推荐做法

1. Entity 使用规范
java 复制代码
✅ DO:
- 只在 DAO/Mapper 层使用
- 字段名与数据库列名对应
- 包含完整的数据库注解
- 不要有业务逻辑代码

❌ DON'T:
- 不要在 Controller 中直接返回 Entity
- 不要在 Entity 中写复杂的方法
- 不要让前端知道 Entity 的结构
2. VO 使用规范
java 复制代码
✅ DO:
- 只包含前端需要的字段
- 敏感字段要脱敏
- 可以包含计算字段
- 可以包含关联数据
- 只在 Controller 返回时使用

❌ DON'T:
- 不要包含密码、盐值等敏感字段
- 不要包含数据库注解
- 不要在 Service 层使用
3. DTO 使用规范
java 复制代码
✅ DO:
- 在 Service 层之间传递数据
- 在微服务之间传递数据
- 可以包含多个 Entity 的数据
- 可以包含业务状态信息

❌ DON'T:
- 不要直接暴露给前端
- 不要包含数据库操作
4. Form 使用规范
java 复制代码
✅ DO:
- 只在 Controller 接收前端数据
- 包含完整的校验注解
- 一个接口一个 Form
- 字段命名清晰

❌ DON'T:
- 不要在 Service 层定义 Form
- 不要在 Form 中写业务逻辑

🔟 对比总结表


|------------|--------|------------|----------------|----------|
| 对象类型 | 作用 | 使用层级 | 主要特点 | 是否包含敏感字段 |
| Entity | 数据库映射 | DAO/Mapper | 与表一一对应,包含数据库注解 | ✅ 是 |
| VO | 返回给前端 | Controller | 只包含展示字段,脱敏处理 | ❌ 否 |
| DTO | 服务间传输 | Service | 跨层传输数据,业务对象 | 🔸 视情况而定 |
| Form | 接收前端数据 | Controller | 包含校验注解,参数验证 | 🔸 可能包含 |


总结:Entity、VO、DTO、Form 是项目分层架构的重要体现,每个对象都有明确的职责。理解它们的区别和转换逻辑,是掌握项目架构的关键!🚀
相关推荐
csdn_life182 小时前
Rustrover 如何像Java一样直接 进行调试和运行
java·开发语言·rust
mike04122 小时前
Eclipse配置Maven详细教程(实测)
java·eclipse·maven
草莓熊Lotso3 小时前
C++11 核心特性实战:列表初始化 + 右值引用与移动语义(附完整代码)
java·服务器·开发语言·汇编·c++·人工智能·经验分享
CoderYanger10 小时前
C.滑动窗口-求子数组个数-越长越合法——2799. 统计完全子数组的数目
java·c语言·开发语言·数据结构·算法·leetcode·职场和发展
C++业余爱好者10 小时前
Java 提供了8种基本数据类型及封装类型介绍
java·开发语言·python
想用offer打牌10 小时前
RocketMQ如何防止消息丢失?
java·后端·架构·开源·rocketmq
皮卡龙10 小时前
Java常用的JSON
java·开发语言·spring boot·json
利刃大大11 小时前
【JavaSE】十三、枚举类Enum && Lambda表达式 && 列表排序常见写法
java·开发语言·枚举·lambda·排序
float_六七11 小时前
Java反射:万能遥控器拆解编程
java·开发语言