【问题标题】:How to convert Optional<Entity> to Optional<EntityDTO> in Spring JPA?如何在 Spring JPA 中将 Optional<Entity> 转换为 Optional<EntityDTO>?
【发布时间】:2021-06-15 06:31:54
【问题描述】:

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

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&lt;Employee&gt;转换为Optional&lt;EmployeeDTO&gt;

2. Spring JPA 是否收集SELECT 子句中的字段,并在服务方法中通过匹配它们的名称将它们映射到对应的DTO?如果是这样,它是否保持命名,例如数据库表和域模型类中的employee_nameemployeeName

【问题讨论】:

  • 如果EmployeeDTO 的构造函数接受Employee 作为参数来构建自己,就像您的代码似乎暗示的那样,仅此而已。 map 在那里返回一个Optional&lt;EmployeeDTO&gt;
  • @FedericoklezCulloca 是的,它接受,但上面的代码抛出不兼容的返回类型。必填:Optional &lt;EmployeeDTO&gt; --> 提供:Stream &lt;Object&gt;
  • .map之前去掉.stream(),我没看到。你想要Optionalmap,而不是Streammap
  • @FedericoklezCulloca 是的,完全正确。我的第二个问题呢?
  • 自从Spring Data already supports projections,你试过Optional&lt;EmployeeDTO&gt; findByUuid(...)吗?

标签: java spring spring-data-jpa optional


【解决方案1】:

employeeRepository#findByUuid 的输出即Optional&lt;Employee&gt; 和方法输出类型Optional&lt;EmployeeDTO&gt; 之间发生的映射是1:1,所以没有Stream(调用stream())这里涉及到。

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

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

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) 进行映射(缺点:创建依赖项)。
  • 只需使用 getter/setter 进行映射。
  • 使用映射框架,例如 MapStruct 或任何其他。

【讨论】:

  • 非常感谢您的完美解释。在这个场景中,如果EmployeeDTO 接受Employee 作为参数,我想我也可以使用.map(EmployeeDTO::new)。这是真的吗?
  • 我认为带有getters/setters 的map 是您代码中的最后3 行,不是吗?如果是这样,它是否也会像.map(EmployeeDTO::new)一样创建依赖关系?
  • 请回复?
  • 1.我已经删除了评论。 2.如果有构造函数public EmployeeDTO(Employee employee),则可以使用.map(EmployeeDTO::new)进行映射。 3. 不,使用 getter/setter 和全参数构造函数,您必须使用以下内容:.map(e -&gt; new EmployeeDTO(e.getId(), e.getName())) 和无参数构造函数:.map(e -&gt; { EmployeeDTO dto = new EmployeeDTO(); dto.setId(e.getId()); dto.setName(e.getName()); return dto; })
  • 你摇滚!。完美的解释,非常感谢。
【解决方案2】:

有多种选项可将您的实体映射到 DTO。

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

并在您的服务中致电employee.map(EmployeeDTO::ofEntity)

【讨论】:

  • 代码中的employee.map(EmployeeDTO::ofEntity 是否类似于.map(EmployeeDTO::new)?有什么区别?
  • 我不能投票,因为我没有足够的 repo :(
  • 请回复?
  • 区别在于EmployeeDTO::new调用了默认构造函数。在某些情况下,您不想将所有实体字段映射到您的 DTO,因此您只需在 ofEntity 静态工厂中映射您需要的字段
  • 非常感谢。请投票,以便我可以投票赞成您的答案?
猜你喜欢
  • 2015-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
  • 2016-02-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多