六边形架构下实体更新异常:调用update新增数据而非更新指定列
问题分析与修复方案
问题根源
更新时新增数据的核心原因是持久化层未操作数据库中已存在的托管实体,反而直接保存了一个未设置ID的新实体:
propertyDatabaseAdapter.update方法中,虽通过exist(propertyId)获取了已存在的领域对象,但完全未用它来更新字段;- 转而将传入的
Property领域对象直接转为实体后调用save,而该Property的ID在映射时被设置为ignore = true,导致实体ID为null; - JPA规则中,
saveID为null的实体时,默认执行插入操作而非更新。
修复步骤
1. 修正持久化层更新逻辑
修改propertyDatabaseAdapter的update方法,改为操作从数据库获取的托管实体:
@Override @Transactional public Property update(Long propertyId, Property property) { // 直接从Repository获取数据库中已存在的托管实体 PropertyEntity existingEntity = propertyRepository.findById(propertyId) .orElseThrow(() -> new ResourceNotFoundException("Property not found with Id " + propertyId)); // 将传入的领域对象的更新字段映射到托管实体上 propertyMapper.updateEntityFromDomain(property, existingEntity); // 保存托管实体(JPA会自动识别为更新操作) PropertyEntity updatedEntity = propertyRepository.save(existingEntity); // 转换为领域对象返回 return propertyMapper.toDomain(updatedEntity); }
2. 新增Mapper的实体更新映射方法
在propertyMapper中添加从领域对象更新实体的映射配置,确保只更新需要修改的字段:
@Mapping(target = "id", ignore = true) @Mapping(target = "yearBuilt", source = "yearBuilt", qualifiedByName = "mapIntegerToYear") @Mapping(target = "propertyType", ignore = true) // 其他需要忽略或自定义映射的字段按需配置 void updateEntityFromDomain(Property source, @MappingTarget PropertyEntity target);
3. 修正控制器响应状态码
更新操作完成后,应返回HttpStatus.OK而非HttpStatus.CREATED:
return ResponseEntity.status(HttpStatus.OK).body(propertyHttpMapper.toDTO(update));
额外说明
- JPA中,托管状态的实体(通过
findById等方法从数据库加载的实体)在修改后,事务提交时会自动同步到数据库; - 若直接创建新实体(即使手动设置ID),若该实体不在EntityManager托管状态中,
save可能执行插入(取决于ID生成策略),正确的更新方式是先加载已有实体,修改字段后再保存。
内容的提问来源于stack exchange,提问作者Nisi Zenuni
相关产品推荐
相关产品推荐

