Spring JPA删除实体后保存抛出EntityNotFoundException解决方案
问题场景
多个数据表通过项目ID关联同一个Project实体,重新加载已存在ID的Project时,需要先清空数据库中该项目下所有关联实体数据。
Controller层代码
@CrossOrigin @RequestMapping(value = "projects", method = RequestMethod.POST) public ResponseEntity<?> uploadProject(MultipartFile file) { JsonDataDto projectDto = converterService.convertToDto(file, JsonDataDto.class); if(projectRepository.exists(projectDto.getId())) { // 从数据库删除该项目下所有关联实体 projectService.delete(projectDto.getId()); } // 将项目保存到数据库 importService.import(projectDto); }
Project Service删除逻辑代码
@Service @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) public class GenericProjectService implements ProjectService { // 成员变量省略 @Override public void delete(UUID projectId) { entity1Repository.deleteByProjectId(projectId) ... // 大部分实体通过外键关联项目,部分无外键的实体(比如Entity1)需要手动调用Repository删除 // 所有关联实体删完后最后删除Project本身 projectRepository.delete(projectId); } }
说明:大部分实体通过外键关联项目,部分无外键的实体(如Entity1)需手动调用Repository删除,最后删除Project本体。
Import Service保存逻辑代码
@Service public class GenericImportService implements ImportService { // 成员变量省略 @Override @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ) public void import(JsonDataDto projectDto) { Collection<Entity1> entity1 = projectDto.getEntity1() .stream().map(e -> e1Repository.save(e1Mapper.to(e))).collect(...); Map<UUID, Type> types = new HashMap<>(); Map<UUID, TypeDto> typeDtosById = projectDto.getTypes().stream() .collect(Collectors.toMap(TypeDto::getId, Function.identity())); for (UUID typeId : typeDtosById.keySet()) { saveType(typeId, typeDtosById, types, ...); } } private void saveType(...) { Type type = new Type(); // 赋值字段、设置关联关系 // 此处抛出异常 type = typeRepository.save(type); types.put(typeId, type); } }
异常在saveType方法执行typeRepository.save(type)时抛出。
Type实体类代码
@Entity @Data @Table(name = "...", schema = "...") public class Type { @Id private TypePK typeId; /* @Data @NoArgsConstructor @AllArgsConstructor @Embeddable public class TypePK implements Serializable { @Type(type = "pg-uuid") @Column(name = "id") private UUID id; @ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER) @JoinColumn(name = "project_id", insertable = false, updatable = false) private Project project; } */ // 其他字段省略 @org.hibernate.annotations.Type(type = "pg-uuid") @Column(name = "parent_id") private UUID parentId; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumns({ @JoinColumn(name = "parent_id", referencedColumnName = "id", updatable = false, insertable = false), @JoinColumn(name = "project_id", referencedColumnName = "project_id", updatable = false, insertable = false)}) private Type parent; }
Type实体使用复合主键TypePK,包含自关联parent字段。
异常现象
- 数据库中不存在对应项目时,保存操作可以正常执行;单独调用删除接口也能正常删除数据库中的项目数据。
- 同一请求流程中先删除已存在的项目,再执行导入保存时,抛出错误:
Unable to find package.Type with id TypePK(id=7e8281fe-77b8-475d-8ecd-c70522f5a403, project=Project(id=8d109d33-e15e-ca81-5f75-09e00a81a194))。 - 抛出异常时对应实体实际已经从数据库中移除,但保存操作的事务会被回滚。
已尝试方案
- 尝试在删除操作后手动调用
TransactionStatus的flush方法强制刷新事务,未生效:
public void delete(UUID projectId) { TransactionStatus ts = TransactionAspectSupport.currentTransactionStatus(); entity1Repository.deleteByProjectId(projectId) ... ts.flush(); }
- 目前仅能通过删除后等待2秒再执行保存的临时方案规避问题,属于权宜之计,需要找到问题根因和规范的解决方案。
内容的提问来源于stack exchange,提问作者Shadow4571
相关产品推荐
相关产品推荐

