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
旁注中有一个相关问题,还需要考虑到:当我想保存父实体时,我不想以任何方式触摸、保存或更改已经存在的子实体。这在 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)