如何在 Spring JPA 中将 Optional<Entity> 转换为 Optional<EntityDTO>?

How to convert Optional<Entity> to Optional<EntityDTO> in Spring JPA?

我是 Spring 的新手,虽然我可以将域实体转换为 List<Entity>,但我无法将它们正确转换为 Optional<Entity>。我在存储库和服务中有以下方法:

EmployeeRepository:

@Query(value = "SELECT ...")
Optional<Employee> findByUuid(@Param(value = "uuid") final UUID uuid);

员工服务:

@Override
@LogExecution
@Transactional(readOnly = true)
public Optional<EmployeeDTO> findByUuid(UUID uuid) {
    Optional<Employee> employee = employeeRepository.findByUuid(uuid);
        return employee
                .stream()
                .map(EmployeeDTO::new)
                // .orElse(null);
                //.findFirst(); /// ??? 
    }

我的问题:

1.我应该如何正确地将Optional<Employee>转换为Optional<EmployeeDTO>

2.是否Spring JPA收集SELECT子句中的字段,并在service方法中映射到对应的DTO匹配他们的名字?如果是这样,它是否维护命名,例如employee_nameemployeeName 在数据库 table 和领域模型 class?

employeeRepository#findByUuid 的输出 Optional<Employee> 和方法输出类型 Optional<EmployeeDTO> 之间发生的映射是 1:1,所以这里不涉及Stream(调用stream())。

您只需将 Employee 的字段正确映射到 EmployeeDTO。处理从 employeeRepository#findByUuid 返回的 Optional 实际上为空的情况可以留在可选的后续链中。不需要 orElsefindFirst 调用。

假设以下 类 都具有全参数构造函数和吸气剂:

class Employee {
    private final long id;
    private final String firstName;
    private final String lastName;
}
class EmployeeDTO {
    private final long id;
    private final String name;
    private final String surname;
}

...你可以执行此操作。除了找到一种方法从 Employee 的字段创建 EmployeeDTO 之外,别无他法。如果返回从 employeeRepository 返回的 Optional,则不会发生映射并返回空的 Optional

@Override
@LogExecution
@Transactional(readOnly = true)
public Optional<EmployeeDTO> findByUuid(UUID uuid) {
    return employeeRepository
        .findByUuid(uuid)                             // Optional<Employee>
        .map(emp -> new EmployeeDTO(                  // Optional<EmployeeDTO>
                 emp.getId(),                         // .. id -> id
                 emp.getFirstName(),                  // .. firstName -> name
                 emp.getLastName()));                 // .. lastName -> surname
}

注意:对于 Employee -> EmployeeDTO 映射,我建议选择其中之一:

  • EmployeeDTO 中创建接受 Employee 的构造函数,允许与 .map(EmployeeDTO::new) 映射(缺点:创建依赖项)。
  • 只需映射 getters/setters。
  • 使用 MapStruct 或任何其他映射框架。

有多种方法可以将您的实体映射到 DTO。

  1. 使用投影:您的存储库可以通过使用投影直接return DTO。如果您根本不需要该实体,这可能是最佳选择。您可以在此处找到有关投影的所有信息 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
  2. 使用像 mapstruct or modelmapper 这样的库来生成您的映射代码
  3. 向您的 DTO 添加构造函数或静态工厂方法。像
class EmployeeDTO {
   // fields here ...
   public static EmployeeDTO ofEntity(Employee entity) {
     var dto = new EmployeeDTO();
     // set fields
     return dto;
   }
}

并在您的服务中调用 employee.map(EmployeeDTO::ofEntity)