详解DTO

DTO是什么?

DTO(Data Transfer Object,数据传输对象)是一种数据传输模型,用于在前端与后端或不同服务之间传递数据。它本质上是一个专门用来传输数据的类,不包含任何业务逻辑。

DTO 与实体类 (Entity) 的区别

特点

DTO

实体类(Entity)

用途

专门用于数据传输,简化数据结构,防止暴露内部模型

主要用于表示数据库表的数据模型,包含完整的数据结构

数据字段

通常只保留必要字段,字段较少

字段较多,包含完整的数据库映射信息

业务逻辑

无业务逻辑,仅用于传输数据

可能包含一些业务逻辑

是否与数据库关联

不直接与数据库关联,纯粹为数据传输服务

直接映射数据库表,包含 @Entity、@Table等注解

敏感信息保护

可以隐藏不应暴露的数据 (如密码、加密数据)

可能包含敏感数据,直接传给前端可能有安全风险

为什么要用 DTO?

✅ 1. 安全性

DTO 允许你筛选数据,避免将敏感信息(如密码、加密数据)直接暴露给前端。

✅ 2. 提高性能

DTO 只传输必要数据,减少网络传输的数据量。

✅ 3. 降低耦合

DTO 将业务逻辑、数据模型与数据库模型解耦,使得系统更灵活,方便后期扩展和维护。

✅ 4. 数据转换方便

DTO 允许在返回结果时重新组织数据结构,满足不同的前端需求。

DTO 和实体类的合作模式

🚀 典型流程

1.Controller 层:接收请求数据并将其封装到 DTO 中。

2.Service 层:将 DTO 转换成实体类 (Entity),并执行业务逻辑。

3.Repository (DAO) 层:使用 Entity 与数据库交互。

4.Controller 层:将 Entity 转换回 DTO 并返回给前端。

图解:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

前端 = 前端 = 前端 = 前端 = 前端 = 前端

↓ 请求报文 ↑ 响应报文

Controller层 :接收 [请求报文] → 解析并封装到 [DTO] 中 接收 [DTO] 并转为符合前端要求的 [响应报文]

↓ ↓ ↑

Service层:将 [DTO] 转换为 [Entity] + 执行业务逻辑 将 [Entity] 转换为 [DTO] + 执行业务逻辑 + 数据处理(过滤/转格式等)

↓ ↓ ↑

DAO层: 使用 [Entity] 与数据库交互 读取数据库数据并封装为[Entity]对象

↓ ↓ ↑

数据库 = 数据库 = 数据库 = 数据库 = 数据库

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

代码示例:

🎯 Step 1:实体类(Entity)

@Entity
@Table(name = "orders")
@Data
public class Order {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String orderId;
    private BigDecimal amount;
    private String customerName;
    private String customerPhone;
    private String internalRemark; // 内部备注(不应该返回给前端)
}

🎯 Step 2:DTO 类

@Data
public class OrderDTO {
    private String orderId;
    private BigDecimal amount;
    private String customerName;
}

🚨 注意:DTO 里没有customerPhone、internalRemark,这些数据不需要返回给前端。

🎯 Step 3:Service 层 - DTO 和实体类的转换

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;

    // 新增订单
    public OrderDTO createOrder(OrderDTO orderDTO) {
        // DTO -> Entity (数据转换)
        Order order = new Order();
        order.setOrderId(orderDTO.getOrderId());
        order.setAmount(orderDTO.getAmount());
        order.setCustomerName(orderDTO.getCustomerName());
        
        // 保存到数据库
        Order savedOrder = orderRepository.save(order);

        // Entity -> DTO (数据转换)
        OrderDTO responseDTO = new OrderDTO();
        responseDTO.setOrderId(savedOrder.getOrderId());
        responseDTO.setAmount(savedOrder.getAmount());
        responseDTO.setCustomerName(savedOrder.getCustomerName());

        return responseDTO;
    }
}

🎯 Step 4:Controller 层

@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/create")
    public ResponseEntity<OrderDTO> createOrder(@RequestBody OrderDTO orderDTO) {
        OrderDTO createdOrder = orderService.createOrder(orderDTO);
        return ResponseEntity.ok(createdOrder);
    }
}

🎯 Step 5:请求和响应示例

请求数据(Request Body)

{
    "orderId": "123456",
    "amount": 250.75,
    "customerName": "张三"
}

响应数据(Response Body)

{
    "orderId": "123456",
    "amount": 250.75,
    "customerName": "张三"
}

🚨 注意:customerPhone 和 internalRemark 并没有暴露在响应中。

工具类简化 DTO/Entity 转换 (推荐)

为了减少 DTO 和实体类的字段映射代码,可以使用以下工具:

✅ MapStruct (推荐)

✅ ModelMapper

✅ BeanUtils.copyProperties()(Spring 提供的简单工具)

🎯 使用 MapStruct 示例

DTO 类

@Mapper(componentModel = "spring")
public interface OrderMapper {

    Order toEntity(OrderDTO orderDTO);

    OrderDTO toDTO(Order order);
}

Service 层 (使用 Mapper)

@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private OrderMapper orderMapper;

    public OrderDTO createOrder(OrderDTO orderDTO) {
        Order order = orderMapper.toEntity(orderDTO);
        Order savedOrder = orderRepository.save(order);
        return orderMapper.toDTO(savedOrder);
    }
}

🚨 什么时候用 DTO?

✅ 数据传输时,DTO 提高安全性、减少数据冗余

✅ 多数据源、多业务场景时,DTO 提高了数据灵活性

✅ 在微服务架构中,DTO 非常适合用于服务间通信

🚨 什么时候用 Entity?

✅ 数据库表的持久化操作

✅ 需要与 JPA、MyBatis 等框架交互时

🚨 最佳实践

在 Controller 与 Service 之间,使用 DTO。

在 Service 与 Repository 之间,使用 Entity。

使用工具类(如 MapStruct)简化 DTO ↔ Entity 转换,提高代码的整洁度。

🔍 进一步理解:DTO 的本质

DTO 本质上就是一个数据容器,类似一个快递盒子,负责:

1.装数据(把数据封装起来)。

2.传数据(让 Controller 和前端/其他服务交换数据)。

DTO 里通常不包含复杂的业务逻辑,更多是对数据的组织和筛选。

💡 为什么需要 DTO?光用实体类不行吗?

在很多小项目中,直接用实体类 (Entity) 也可以完成数据传输。但是在以下场景下,DTO 更加合适:

🚨 1. 安全性

  • 实体类往往与数据库结构一一对应,可能包含敏感字段(如密码、加密信息、内部备注等)。
  • DTO 通过数据筛选,仅暴露必要数据,避免敏感信息泄露。

✅ 示例

Entity (实体类)

@Entity
public class User {
    private Long id;
    private String username;
    private String password;    // 🚨 敏感字段
    private String internalNote; // 🚨 内部备注,前端不需要
}

DTO

public class UserDTO {
    private String username;
}

返回结果 (使用 DTO)

{
    "username": "zhangsan"
}

DTO 成功隐藏了password和internalNote,更安全。

🚨 2. 数据结构不一致

  • 前端页面往往不需要完整的实体数据,DTO 能按需组织数据,减少无关数据的传输。
  • 复杂的业务场景下,DTO 可以将多张表的数据整合到一个对象中,提高数据传输效率。

✅ 示例:数据整合假设前端需要订单信息+客户信息,DTO 就可以将两部分数据整合成一个对象:

OrderDTO (整合了订单和客户信息)

public class OrderDTO {
    private String orderId;
    private BigDecimal totalAmount;
    private String customerName;
    private String customerPhone;
}

在Service层将Order、Customer数据整合到OrderDTO中,避免前端多次请求。

🚨 3. 数据转换

  • DTO 允许在返回数据时更改字段名、重新组合数据,满足不同客户端的需求。
  • DTO 也可以对数据做格式转换,例如时间格式转换、金额格式化等。

✅ 示例:时间格式转换Entity (数据库中的时间戳)

private LocalDateTime createTime; // 数据库存储

DTO (转换为字符串形式)

private String createTime; // 返回给前端的格式

在 Service 层将 LocalDateTime 转换为 String,让前端更容易展示。

🚨 4. API 版本控制

  • DTO 便于在接口升级时增加新字段或调整数据结构,而不影响原有实体类。
  • 通过 DTO,接口兼容性更强,便于维护。

✅ 示例:API 版本升级

public class UserV1DTO {
    private String username;
}

public class UserV2DTO {
    private String username;
    private String email;  // 新增字段
}

🚨 5. 解耦性

  • Entity 类通常与数据库表结构密切相关,DTO 则是纯粹的数据模型,与持久层逻辑无关。
  • DTO 解耦了数据模型与业务逻辑,避免前端直接接触数据库模型,提高系统的稳定性和灵活性。

⚙️ 总结:DTO 的本质

✅ DTO 主要用于数据传输。

✅ DTO 通过筛选、转换、整合数据,满足前端或其他服务的需求。

✅ DTO 能够避免将敏感信息暴露在 API 接口中,提高安全性。

虽然 DTO 只是一个“装数据的盒子”,但它的作用非常重要,尤其在大型项目、复杂业务场景下,DTO 能显著提升代码的安全性、灵活性和可维护性。

全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

更多
正在热议
更多
牛客网
牛客企业服务