JPA多对多ManyToMany关联下保存Agency后无法获取Client实体ID
问题根因
- 你使用
agencyService.getOne()获取的是JPA懒加载代理对象,代理对象的集合操作可能导致持久化上下文的实例属性同步延迟。 - 你直接持有手动new的Client实例取ID,级联保存虽然会填充持久化上下文中Client实例的ID,但受HashSet存储逻辑、代理对象实例隔离的影响,你持有的原始实例不会被同步赋值。
- 额外隐患:你Client实体的
equals和hashCode基于name字段生成,若存在重复name会导致Agency的clients集合中存入重复Client数据,也会影响实例匹配。
解决方案
方案1(保留级联保存逻辑,改动最小)
修改业务代码如下:
@Override @Transactional public Long save(ClientCreateDto clientCreateDto) { try { Client client = new Client(); client.setName(clientCreateDto.getName()); Address address = new Address(); address.setFirstLine(clientCreateDto.getAddress().getFirstLine()); address.setTown(clientCreateDto.getAddress().getTown()); address.setPostcode(clientCreateDto.getAddress().getPostcode()); client.setAddress(address); client.setEmail(clientCreateDto.getEmail()); client.setBillingEmail(clientCreateDto.getBillingEmail()); client.setTelephone(clientCreateDto.getTelephone()); client.setLogo(clientCreateDto.getLogo()); client.setCreatedBy("System"); client.setStatus(Status.ACTIVE); client.setService(service.getOne(clientCreateDto.getServiceId())); // 替换getOne为findById,获取真实实体对象而非代理 Agency agency = agencyRepository.findById(clientCreateDto.getAgencyId()) .orElseThrow(() -> new BusinessValidationException("机构不存在")); agency.addClient(client); agencyRepository.saveAndFlush(agency); // 从agency的clients集合中匹配获取已持久化的client实例取ID return agency.getClients().stream() .filter(c -> c.getName().equals(client.getName())) .findFirst() .orElseThrow(() -> new BusinessValidationException("客户保存失败")) .getId(); } catch (Exception exception) { throw new BusinessValidationException(exception.getMessage()); } }
如果要更稳妥,可以在Agency的@ManyToMany注解的级联配置中追加CascadeType.REFRESH:
@ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }) @JoinTable(name = "agency_client", joinColumns = @JoinColumn(name = "agency_id"), inverseJoinColumns = @JoinColumn(name = "client_id") ) private Set<Client> clients = new HashSet<>();
方案2(性能更稳定,避免级联坑)
不依赖级联保存Client,先单独持久化Client再关联:
@Override @Transactional public Long save(ClientCreateDto clientCreateDto) { try { Client client = new Client(); client.setName(clientCreateDto.getName()); Address address = new Address(); address.setFirstLine(clientCreateDto.getAddress().getFirstLine()); address.setTown(clientCreateDto.getAddress().getTown()); address.setPostcode(clientCreateDto.getAddress().getPostcode()); client.setAddress(address); client.setEmail(clientCreateDto.getEmail()); client.setBillingEmail(clientCreateDto.getBillingEmail()); client.setTelephone(clientCreateDto.getTelephone()); client.setLogo(clientCreateDto.getLogo()); client.setCreatedBy("System"); client.setStatus(Status.ACTIVE); client.setService(service.getOne(clientCreateDto.getServiceId())); // 先保存client,直接拿到ID client = clientRepository.saveAndFlush(client); Agency agency = agencyRepository.findById(clientCreateDto.getAgencyId()) .orElseThrow(() -> new BusinessValidationException("机构不存在")); agency.addClient(client); agencyRepository.saveAndFlush(agency); return client.getId(); } catch (Exception exception) { throw new BusinessValidationException(exception.getMessage()); } }
内容的提问来源于stack exchange,提问作者Mapfunde Venon
相关产品推荐
相关产品推荐

