含外键属性的类映射异常:重复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
相关产品推荐
相关产品推荐

