如何创建引用spring数据rest/HATEOAS中已存在的子实体的新父实体

Rob*_*ert 8 java spring spring-data-rest spring-hateoas

在我的项目中,我有两个域模型.父母和子实体.父级引用子权利列表.(例如,帖子和评论)两个实体都有它们的弹簧数据JPA CrudRepository<Long, ModelClass>接口,它们被公开为@RepositoryRestResource

HTTP GET和PUT操作正常工作,并返回这些模型的漂亮HATEOS表示.

现在我需要一个特殊的REST端点"创建一个引用一个或多个现有子实体的新Parent ".我想将对子项的引用作为text/uri-list发布,我在请求正文中传递,如下所示:

POST http://localhost:8080/api/v1/createNewParent
HEADER
  Content-Type: text/uri-list
HTTP REQUEST BODY:
   http://localhost:8080/api/v1/Child/4711
   http://localhost:8080/api/v1/Child/4712
   http://localhost:8080/api/v1/Child/4713
Run Code Online (Sandbox Code Playgroud)

如何实现此休止端点?这是我到目前为止所尝试的:

 @Autowired
 ParentRepo parentRepo  // Spring Data JPA repository for "parent" entity


 @RequestMapping(value = "/createNewParent", method = RequestMethod.POST)
 public @ResponseBody String createNewParentWithChildren(
    @RequestBody Resources<ChildModel> childList,                         
    PersistentEntityResourceAssembler resourceAssembler
) 
{
   Collection<ChildModel> childrenObjects = childList.getContent()

   // Ok, this gives me the URIs I've posted
   List<Link> links = proposalResource.getLinks();

   // But now how to convert these URIs to domain objects???
   List<ChildModel> listOfChildren = ... ???? ...

   ParentModel newParnet = new ParentModel(listOfChildren)
   parentRepo.save(newParent)

}
Run Code Online (Sandbox Code Playgroud)

参考/相关 https://github.com/spring-projects/spring-hateoas/issues/292

Rob*_*ert 2

旁注中有一个相关问题,还需要考虑到:当我想保存父实体时,我不想以任何方式触摸、保存或更改已经存在的子实体。这在 JPA 中并不那么容易。因为 JPA 还将(尝试)保留依赖的子实体。此操作失败,但有例外:

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist:
Run Code Online (Sandbox Code Playgroud)

为了避免这种情况,您必须将子实体合并到 JPA save() 调用的事务中。我发现在一个事务中拥有两个实体的唯一方法是创建一个单独的@Services,并将其标记为@Transactional。看起来完全是过度杀伤和过度设计。

这是我的代码:

PollController.java // 父实体的自定义 REST 端点

@BasePathAwareController
public class PollController {

@RequestMapping(value = "/createNewPoll", method = RequestMethod.POST)
public @ResponseBody Resource createNewPoll(
    @RequestBody Resource<PollModel> pollResource, 
    PersistentEntityResourceAssembler resourceAssembler
) throws LiquidoRestException
{
  PollModel pollFromRequest = pollResource.getContent();
  LawModel proposalFromRequest = pollFromRequest.getProposals().iterator().next();             // This propsal is a "detached entity". Cannot simply be saved.
  //jpaContext.getEntityManagerByManagedType(PollModel.class).merge(proposal);      // DOES NOT WORK IN SPRING.  Must handle transaction via a seperate PollService class and @Transactional annotation there.

  PollModel createdPoll;
  try {
    createdPoll = pollService.createPoll(proposalFromRequest, resourceAssembler);
  } catch (LiquidoException e) {
    log.warn("Cannot /createNewPoll: "+e.getMessage());
    throw new LiquidoRestException(e.getMessage(), e);
  }

  PersistentEntityResource persistentEntityResource = resourceAssembler.toFullResource(createdPoll);

  log.trace("<= POST /createNewPoll: created Poll "+persistentEntityResource.getLink("self").getHref());

  return persistentEntityResource;   // This nicely returns the HAL representation of the created poll
}
Run Code Online (Sandbox Code Playgroud)

PollService.java // 用于事务处理

@Service
public class PollService {

    @Transactional    // This should run inside a transaction (all or nothing)
    public PollModel createPoll(@NotNull LawModel proposal, PersistentEntityResourceAssembler resourceAssembler) throws LiquidoException {
    //===== some functional/business checks on the passed enties (must not be null etc)
    //[...]

    //===== create new Poll with one initial proposal
    log.debug("Will create new poll. InitialProposal (id={}): {}", proposal.getId(), proposal.getTitle());
    PollModel poll = new PollModel();
    LawModel proposalInDB = lawRepo.findByTitle(proposal.getTitle());  // I have to lookup the proposal that I already have

    Set<LawModel> linkedProposals = new HashSet<>();
    linkedProposals.add(proposalInDB);
    poll.setProposals(linkedProposals);

    PollModel savedPoll = pollRepo.save(poll);

    return savedPoll;
}
Run Code Online (Sandbox Code Playgroud)