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

单向@ManyToOne关联下AccountRequestStatus实体更新失败问题咨询

问题根因
  • 核心逻辑错误:更新AccountRequestStatus时未查询已持久化的旧记录,直接新建了无主键ID的全新实体对象,JPA的save()方法会将无ID的实体识别为新数据,执行INSERT操作而非UPDATE操作:
    1. 首次保存INITIAL状态时,AccountRequest先被持久化拿到数据库生成的主键,关联创建的AccountRequestStatus也会被生成主键,正常入库。
    2. 第二个服务中直接从MQ事件转换得到的AccountRequest没有携带数据库生成的主键ID,基于此新建的AccountRequestStatusDto的ID字段为空,Mapper转换后的实体无ID,执行save只会插入新记录,不会更新原有INITIAL状态的记录。
  • 缺少唯一性校验:如果T_ACCOUNT_REQUEST_STATUS表未对关联的ACCOUNT_REQUEST字段加唯一约束,重复插入不会报错,只会生成多条状态不同的关联记录,看起来就像更新没生效;如果加了唯一约束,会直接抛出主键/唯一键冲突异常,未捕获对应异常就会感知不到报错。
  • 第二个服务中未先查询已存在的AccountRequest:直接从事件转换得到的AccountRequest仅包含业务字段,没有数据库主键,无法关联到已有的状态记录。
解决方案

步骤1:新增Repository查询方法

在AccountRequestRepository中新增业务键查询方法:

Optional<AccountRequest> findByMessageIdAndAccountNo(String messageId, String accountNo);

在AccountRequestStatusRepository中新增关联查询方法:

Optional<AccountRequestStatus> findByAccountRequestId(Long accountRequestId);

步骤2:修改第二个服务的状态保存逻辑

先查询已存在的AccountRequest,获取数据库主键:

public void saveSuccessfulAccountStatus(Event event) {
    AccountRequest temp = eventToAccountRequestMapper.eventToAccountRequest(event, eventArgumentKey);
    // 先查库拿到已持久化的AccountRequest,获取主键
    AccountRequest existAccountRequest = accountRequestRepository.findByMessageIdAndAccountNo(temp.getMessageId(), temp.getAccountNo())
            .orElseThrow(() -> new IllegalArgumentException("未找到对应账户申请记录,messageId:" + temp.getMessageId() + ", accountNo:" + temp.getAccountNo()));
    AccountRequestDto accountRequestDto = accountRequestMapper.toDto(existAccountRequest);
    saveAccountRequestStatus(accountRequestDto, AccountRequestStatusEnum.SUCCESS);
}

步骤3:改造AccountRequestStatus保存逻辑,兼容新增/更新场景

修改AccountRequestStatusServiceImpl的保存方法,自动判断是新增还是更新:

@Override
@Transactional
public void saveAccountRequestStatus(AccountRequestStatusDto accountRequestStatusDto) {
    Long accountRequestId = accountRequestStatusDto.getAccountRequestDto().getId();
    // 先查询是否已有对应状态记录
    AccountRequestStatus status = accountRequestStatusRepository.findByAccountRequestId(accountRequestId)
            // 无记录则为首次保存,用Mapper转换新建实体
            .orElseGet(() -> accountRequestStatusMapper.toAccountRequestStatus(accountRequestStatusDto));
    // 已有记录则仅更新状态字段
    if (status.getId() != null) {
        status.setAccountRequestStatusEnum(accountRequestStatusDto.getAccountRequestStatusEnum());
    }
    accountRequestStatusRepository.save(status);
}

可选优化

可以给T_ACCOUNT_REQUEST_STATUS表的ACCOUNT_REQUEST字段加唯一约束,避免一个AccountRequest对应多条状态记录,出现数据不一致问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 17:57:04