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

含外键属性的类映射异常:重复Provider ID致数据更新而非新增

问题描述

我有Hardware实体、HardwareDtoRequest和HardwareDtoResponse类,使用ModelMapper进行对象映射。Hardware表存在指向Provider表的外键,当前遇到的问题是外键属性无法正确映射:通过Postman调用POST接口,请求体传入必填的name和provider_id时,首次请求能正常保存记录,但第二次传入相同的name和provider_id组合时,系统会更新旧记录而非新增新记录。请问如何正确映射外键属性并实现正常的新增保存?


相关代码

Hardware.java

@Getter
@Setter
@Entity
public class Hardware {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

    @ManyToOne
    @JoinColumn(name = "provider_id")
    private Provider provider;

}

Provider.java

@Getter
@Setter
@Entity
public class Provider {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 100)
    private String name;

}

HardwareDtoRequest.java

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class HardwareDtoRequest {

    @NotNull(message = "required field")
    private String name;

    @NotNull(message = "required field")
    private Long providerId;

}

HardwareDtoResponse.java

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class HardwareDtoResponse {

    private Long id;
    private String name;
    private ProviderDtoResponse provider;

}

HardwareMapper.java

public HardwareDtoResponse toHardwareDtoResponse(Hardware hardware) {
        
    return mapper.map(hardware, HardwareDtoResponse.class);
}
    
public Hardware toHardware(HardwareDtoRequest hardwareDtoRequest) {
        
    return mapper.map(hardwareDtoRequest, Hardware.class);
}

HardwareService.java

@Transactional
public HardwareDtoResponse save(HardwareDtoRequest hardwareDtoRequest) {
        
    Hardware hardware = mapper.toHardware(hardwareDtoRequest);
        
    Hardware saveHardware = hardwareRepository.save(hardware);

    return mapper.toHardwareDtoResponse(saveHardware);
}

HardwareController.java

@PostMapping
public ResponseEntity<HardwareDtoResponse> save(@Valid @RequestBody HardwareDtoRequest hardwareDtoRequest) {
        
    log.info("saving hardware: {}", hardwareDtoRequest);    

    HardwareDtoResponse hardware = hardwareService.save(hardwareDtoRequest);
        
    return new ResponseEntity<>(hardware, HttpStatus.CREATED);
}

解决方案

1. 手动关联数据库中已存在的Provider实体

ModelMapper默认会生成一个仅含id的临时Provider对象,这种游离状态的实体可能导致JPA状态管理异常。正确的做法是从数据库查询已存在的Provider实例,再关联到Hardware对象:

修改HardwareMapper.java的toHardware方法(需注入ProviderRepository):

@Autowired
private ProviderRepository providerRepository;

public Hardware toHardware(HardwareDtoRequest hardwareDtoRequest) {
    Hardware hardware = mapper.map(hardwareDtoRequest, Hardware.class);
    // 查询对应ID的Provider,不存在则抛出参数异常
    Provider provider = providerRepository.findById(hardwareDtoRequest.getProviderId())
            .orElseThrow(() -> new IllegalArgumentException("Provider不存在,ID:" + hardwareDtoRequest.getProviderId()));
    hardware.setProvider(provider);
    return hardware;
}

2. 配置ModelMapper避免自动映射Provider

为了防止ModelMapper自动生成临时Provider对象,可添加显式映射规则,跳过Provider的自动映射:

// 在Mapper初始化时添加配置
mapper.typeMap(HardwareDtoRequest.class, Hardware.class)
        .addMapping(HardwareDtoRequest::getName, Hardware::setName)
        .addMappings(mapping -> mapping.skip(Hardware::setProvider));

3. 确认JPA保存逻辑的正确性

Spring Data JPA的save()方法会根据实体的id是否为null判断执行插入还是更新。由于HardwareDtoRequest中没有id字段,映射后的Hardware对象id应为null,此时save()会执行插入。若仍出现更新,可排查:

  • 是否存在EntityManager缓存的旧Hardware实例,可尝试使用saveAndFlush()强制刷新
  • 数据库表是否存在name+provider_id的联合唯一约束,若有需删除或调整业务逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 06:05:14