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

