在这个涉及将实体 class 实例转换为 DTO 实例的用例中,如何正确使用流 map()?

How can I correctly use the stream map() in this use case involving the conversion of an entity class instance into a DTO instance?

我正在开发一个 Spring 引导应用程序,我正在尝试使用更现代的(Java 8+ 构造),但我发现在尝试对我的应用程序进行以下更改时遇到一些困难现有代码。

我有以下服务方式:

@Override
public Optional<User> activateDeactivateUser(int userId, boolean isActive) throws NotFoundException {
    
    Optional<User> retrievedUser = this.userRepository.findById(userId);
    
    if(retrievedUser.isEmpty())
        throw new NotFoundException(String.format("The user having ID or email %s was not found", userId));
    
    
    return Optional.ofNullable(retrievedUser) 
            .map((user) -> { 
                log.info(String.format("****** USER E-MAIL *******", user.get().getEmail()));
                
                user.get().set_active(isActive);
                this.userRepository.save(user.get());
                
                return user;
                })
            .orElseThrow(() -> new NotFoundException(String.format("The user having ID or email %s was not found", userId)));
}

如您所见,此方法 return 一个 可选 对象。首先,我不确定 returning Optional 是一个好习惯(因为如果它是空的,我将抛出并处理 NotFoundException).

但主要问题是我的 User class 是一个实体 class(我使用的是 Hibernate,所以它包含 table映射)我想更改以前的方法以检索 UserDTO 对象。

为了将 User 实例转换为 UserDTO 实例,我有这个 ConversionService注入到我的服务中class(我在别处使用过)

@Autowired
ConversionService conversionService;

我的主要问题是,在我以前的方法中,我使用的是 map() 运算符。我试过这样改变以前的服务方式:

@Override
public UserDTO activateDeactivateUser(int userId, boolean isActive) throws NotFoundException {
    
    Optional<User> retrievedUser = this.userRepository.findById(userId);
    User userToBeUpdated = null;
    
    if(retrievedUser.isEmpty())
        throw new NotFoundException(String.format("The user having ID or email %s was not found", userId));
    else
        userToBeUpdated = retrievedUser.get(); 
    
    return userToBeUpdated
            .map((userToBeUpdated) -> { 
                log.info(String.format("****** USER E-MAIL *******", userToBeUpdated.getEmail()));
                
                userToBeUpdated.set_active(isActive);
                this.userRepository.save(userToBeUpdated);
                
                return userToBeUpdated;
                })
            .orElseThrow(() -> new NotFoundException(String.format("The user having ID or email %s was not found", userId)));

}

我的想法是使用 map() 方法将一个对象(我的 User 对象转换为 UserDTO 对象并将其保存在应用函数的 DB 上。

首先,Eclipse 在 map() 方法调用中出现以下错误:

The method map(( userToBeUpdated) -> {}) is undefined for the type User

由于我对流的经验很少,所以我不知道它是否是 map() 使用的正确用例。这是因为(同样在第一个方法实现中,它工作正常)我没有实现真正的对象转换,但我正在更改对象字段的值,然后将它保存到数据库中。在第二种情况下(我的服务方法的第二个版本)我必须从 User 转换为 UsertDTO.

我错过了什么?在这个用例中使用 map() 是合法的还是强制使用 map() 的预期方式?什么是解决这个问题的好方法?

在第一个示例中,if 语句完全没有意义。您可以完全放弃它,因为如果返回的实际值不是 nullOptional 上的 map 将被执行。此外,NotFoundException.

存在代码重复
@Override
public UserDTO activateDeactivateUser(int userId, boolean isActive) throws NotFoundException {
    return this.userRepository.findById(userId)
            .map((userToBeUpdated) -> {
                log.info(String.format("****** USER E-MAIL *******", userToBeUpdated.getEmail()));

                userToBeUpdated.set_active(isActive);
                this.userRepository.save(userToBeUpdated);

                return userToBeUpdated;
            })
            .orElseThrow(() -> new NotFoundException(String.format("The user having ID or email %s was not found", userId)));

}

尽管此代码可能有效,但问题是我们在 map 的 lambda 内部有一个副作用。当我们将其作为映射操作的高阶函数传递时,建议使用纯函数。使用这样的东西可能会更好:

@Override
public User activateDeactivateUser(int userId, boolean isActive) throws NotFoundException {

    User retrievedUser = this.userRepository.findById(userId)
            .orElseThrow(() -> new NotFoundException(String.format("The user having ID or email %s was not found", userId)));

    retrievedUser.set_active(isActive);
    this.userRepository.save(retrievedUser);

    return retrievedUser;
}

现在,你的第二个例子更有问题。此代码段无效且逻辑上没有意义:

userToBeUpdated
            .map((userToBeUpdated) -> {...})

User class 是一个休眠实体,它很可能没有实现 map 方法,为什么会这样?在 Java 中,流和可选项通常为 map 提供实现。它用于对 stream/optional 封装的每个对象应用一些转换。您可能需要查看 streams and optionals 的文档以了解有关它们的更多信息。

我相信你想在这里做的是:

@Override
public UserDTO activateDeactivateUser(int userId, boolean isActive) throws NotFoundException {

    User retrievedUser = this.userRepository.findById(userId)
            .orElseThrow(() -> new NotFoundException(String.format("The user having ID or email %s was not found", userId)));

    retrievedUser.set_active(isActive);
    this.userRepository.save(retrievedUser);

    return conversionService.convert(retrievedUser);
}

我希望 ConversionService 有类似 convert 方法的东西,它接受一个实体对象和 returns 一个 DTO。

其他与问题无关的注释:

  • 请遵守 Java 编码标准。在 Java 中,我们不使用 snake case 进行方法调用。这个userToBeUpdated.set_active(isActive);应该是这样的userToBeUpdated.setActive(isActive);