You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java函数式If-Then-Else实现异常:凭证获取总抛错求排查

问题描述

现有两个获取用户凭证的Function实现:

从主数据库获取用户凭证

private Function<Long, Optional<UserCredentials>>
    getUserCredentialsFromMaster() {
        return userId -> Optional.ofNullable(userId)
            .flatMap(masterUserRepository::findById)
            .map(User::getCredentials);
}

从备用数据库获取用户凭证

private Function<Long, Optional<UserCredentials>>
    getUserCredentialsFromSecondary() {
        return userId -> Optional.ofNullable(userId)
            .flatMap(secondaryUserRepository::findById)
            .map(User::getCredentials);
}

领域类UserProfile定义如下:

public class UserProfile {
    Long id;
    Long internalUserId; // internalUserId非空则externalUserId为空,反之亦然
    Long externalUserId;
}

尝试根据userId来源调用对应方法获取凭证,代码如下:

final UserProfile userProfile = userProfileRepository.findById(userProvileId);

final UserCredentials userCredentials =
    Optional.ofNullable(userProfile.internalUserId)
        .flatMap(getUserCredentialsFromMaster())
        .orElse(
            Optional.ofNullable(userProfile.externalUserId)
                .flatMap(getUserCredentialsFromSecondary())
                .orElseThrow(UserCredentialsNotFound::new));

当前问题:internalUserId不为null,但代码始终抛出UserCredentialsNotFound异常;改用普通if-then-else调用则可正常执行。

问题原因
  1. 业务逻辑不匹配:
    根据规则,internalUserId非空时externalUserId必然为空。当internalUserId存在但主库未查到对应凭证时,flatMap(getUserCredentialsFromMaster())会返回Optional.empty(),此时代码会进入orElse分支尝试查询备用库,但externalUserId为空导致备用库查询也返回空,最终触发orElseThrow。而if-then-else逻辑中,internalUserId存在时只会尝试主库查询,查不到直接抛异常,不会执行备用库查询。

  2. orElse使用错误:
    orElse的参数是立即求值的,无论前面的Optional是否有值,Optional.ofNullable(userProfile.externalUserId)...orElseThrow(...)这段代码都会提前执行,既不符合业务逻辑,还会造成不必要的数据库查询。

解决方案

方案1:回归if-then-else(最直观,符合业务逻辑)

final UserProfile userProfile = userProfileRepository.findById(userProvileId);

final UserCredentials userCredentials;
if (userProfile.internalUserId != null) {
    // internalUserId存在时,仅查询主库,查不到直接抛异常
    userCredentials = getUserCredentialsFromMaster()
        .apply(userProfile.internalUserId)
        .orElseThrow(UserCredentialsNotFound::new);
} else {
    // 仅当internalUserId为空时,查询备用库
    userCredentials = getUserCredentialsFromSecondary()
        .apply(userProfile.externalUserId)
        .orElseThrow(UserCredentialsNotFound::new);
}

方案2:使用Optional链式调用(保持函数式风格)

用orElseGet替代orElse实现延迟执行,同时调整逻辑确保internalUserId存在时仅查询主库:

final UserProfile userProfile = userProfileRepository.findById(userProvileId);

final UserCredentials userCredentials = Optional.ofNullable(userProfile.internalUserId)
    .map(userId -> getUserCredentialsFromMaster().apply(userId)
        .orElseThrow(UserCredentialsNotFound::new))
    .orElseGet(() -> Optional.ofNullable(userProfile.externalUserId)
        .map(userId -> getUserCredentialsFromSecondary().apply(userId)
            .orElseThrow(UserCredentialsNotFound::new))
        .orElseThrow(UserCredentialsNotFound::new));

内容的提问来源于stack exchange,提问作者j3d

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 12:35:28