Hibernate @OneToMany引用的子项未更新/丢失?

huangapple go评论105阅读模式
英文:

Hibernate @OneToMany reference child not updated / lost?

问题

以下是要翻译的代码部分:

  1. Say I have three entities.
  2. @Entity
  3. public class Process {
  4. @Id
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)
  6. private Long id;
  7. @Column(unique = true)
  8. private String name;
  9. @ManyToAny(
  10. metaColumn = @Column(name = "node_type"),
  11. fetch = FetchType.LAZY
  12. )
  13. @AnyMetaDef(
  14. idType = "long", metaType = "string",
  15. metaValues = {
  16. @MetaValue(targetEntity = Milestone.class, value = MILESTONE_DISC),
  17. @MetaValue(targetEntity = Phase.class, value = PHASE_DISC)
  18. }
  19. )
  20. @Cascade({org.hibernate.annotations.CascadeType.ALL})
  21. @JoinTable(
  22. name = "process_nodes",
  23. joinColumns = @JoinColumn(name = "process_id", nullable = false),
  24. inverseJoinColumns = @JoinColumn(name = "node_id", nullable = false)
  25. )
  26. private Collection<ProcessNode> nodes = new ArrayList<>();
  27. ...
  28. }
  29. @Entity
  30. @ToString
  31. @DiscriminatorValue(MILESTONE_DISC)
  32. public class Milestone implements ProcessNode {
  33. @Id
  34. @GeneratedValue(strategy = GenerationType.IDENTITY)
  35. private Long id;
  36. private String name;
  37. @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  38. private Collection<ResultDefinition> results;
  39. @ManyToOne()
  40. private Process process;
  41. ...
  42. }
  43. @Entity
  44. @ToString
  45. public class ResultDefinition {
  46. @Id
  47. @GeneratedValue(strategy = GenerationType.IDENTITY)
  48. private Long id;
  49. private String externalId;
  50. private String name;
  51. private ResultType resultType;
  52. }
  1. From my client I want to add an Object of type ResultDefinition to a Milestone in a Process like this:
  2. @Transactional
  3. @PostMapping("/{milestone_id}/results")
  4. public ResultDefinitionDto createResult(@PathVariable("milestone_id") Long milestoneId, @RequestBody ResultDefinitionDto dto) {
  5. Process foundProcess = getProcess(milestoneId);
  6. checkFoundProcess(milestoneId, foundProcess);
  7. Milestone milestone = getMilestone(foundProcess, milestoneId);
  8. ResultDefinition resultDefinition = resultDefinitionMapper.fromDTO(dto);
  9. milestone.addResult(resultDefinition);
  10. processService.save(foundProcess);
  11. //TODO: Find out why this is necessary (???)
  12. ResultDefinition savedResult = milestone.getResult(resultDefinition.getName());
  13. return resultDefinitionMapper.fromEntity(savedResult);
  14. }
  1. In my method createResult I add resultDefinition to the milestone results collection.
  2. When I save the parent foundProcess, I see that foundprocess->milestone->resultDefinition gets persisted and gets an ID. When I call resultDefinition.getId() it returns null. Also, the ResultDefinition Object in the foundProcess is another reference and not the same that I added to milestone.results.
  3. Why do I get the correct instance when calling milestone.getResult()?
  1. Edit: my implementation of processService / repository
  2. @Override
  3. public Process save(Process entity) {
  4. return processRepository.saveAndFlush(entity);
  5. }
  1. public interface ProcessRepository extends JpaRepository<Process, Long>, JpaSpecificationExecutor<Process> {
  2. ...
  3. }
英文:

Say I have three entities.

  1. @Entity
  2. public class Process {
  3. @Id
  4. @GeneratedValue(strategy = GenerationType.IDENTITY)
  5. private Long id;
  6. @Column(unique = true)
  7. private String name;
  8. @ManyToAny(
  9. metaColumn = @Column(name = &quot;node_type&quot;),
  10. fetch = FetchType.LAZY
  11. )
  12. @AnyMetaDef(
  13. idType = &quot;long&quot;, metaType = &quot;string&quot;,
  14. metaValues = {
  15. @MetaValue(targetEntity = Milestone.class, value = MILESTONE_DISC),
  16. @MetaValue(targetEntity = Phase.class, value = PHASE_DISC)
  17. }
  18. )
  19. @Cascade({org.hibernate.annotations.CascadeType.ALL})
  20. @JoinTable(
  21. name = &quot;process_nodes&quot;,
  22. joinColumns = @JoinColumn(name = &quot;process_id&quot;, nullable = false),
  23. inverseJoinColumns = @JoinColumn(name = &quot;node_id&quot;, nullable = false)
  24. )
  25. private Collection&lt;ProcessNode&gt; nodes = new ArrayList&lt;&gt;();
  26. ...
  27. }

  1. @Entity
  2. @ToString
  3. @DiscriminatorValue(MILESTONE_DISC)
  4. public class Milestone implements ProcessNode {
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.IDENTITY)
  7. private Long id;
  8. private String name;
  9. @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  10. private Collection&lt;ResultDefinition&gt; results;
  11. @ManyToOne()
  12. private Process process;
  13. ...
  14. }

  1. @Entity
  2. @ToString
  3. public class ResultDefinition {
  4. @Id
  5. @GeneratedValue(strategy = GenerationType.IDENTITY)
  6. private Long id;
  7. private String externalId;
  8. private String name;
  9. private ResultType resultType;
  10. }

From my client I want to add an Object of type ResultDefinition to a Milestone in a Process like this:

  1. @Transactional
  2. @PostMapping(&quot;/{milestone_id}/results&quot;)
  3. public ResultDefinitionDto createResult(@PathVariable(&quot;milestone_id&quot;) Long milestoneId, @RequestBody ResultDefinitionDto dto) {
  4. Process foundProcess = getProcess(milestoneId);
  5. checkFoundProcess(milestoneId, foundProcess);
  6. Milestone milestone = getMilestone(foundProcess, milestoneId);
  7. ResultDefinition resultDefinition = resultDefinitionMapper.fromDTO(dto);
  8. milestone.addResult(resultDefinition);
  9. processService.save(foundProcess);
  10. //TODO: Find out why this is necessary (???)
  11. ResultDefinition savedResult = milestone.getResult(resultDefinition.getName());
  12. return resultDefinitionMapper.fromEntity(savedResult);
  13. }

In my method createResult I add resultDefinition to the milestone results collection.
When I save the parent foundProcess, I see that foundprocess->milestone->resultDefinition get's persisted and gets an ID. When I call resultDefinition.getId() it returns null. Also the ResultDefinition Object in the foundProcess is another reference and not the same that I added to milestone.results.

Why do I get the correct instance when calling milestone.getResult()?


Edit: my implementation of processService / repository

  1. @Override
  2. public Process save(Process entity) {
  3. return processRepository.saveAndFlush(entity);
  4. }

  1. public interface ProcessRepository extends JpaRepository&lt;Process, Long&gt;, JpaSpecificationExecutor&lt;Process&gt; {
  2. ...
  3. }

答案1

得分: 1

ResultDefinition 在保存过程中被替换。临时实体被插入到数据库中,将通过具有标识的托管实体替换。您在 createResult 方法中对 ResultDefinition 的引用仍然指向临时实体。这就是为什么您必须使用从保存调用中返回的实体的原因。

在您的情况下,您正在保存父进程。因此,您必须通过进程或里程碑实体访问保存的 ResultDefinition。

英文:

The ResultDefinition gets replaced during the save process. The transient entity gets inserted into the database and will be replaced through a managed entity with an id. Your reference to the ResultDefinition in the createResult method still points to the transient one. That´s why you have to work with the returned entities from a save call.

In your case you are saving the parent process. So you have to access the saved ResultDefinition through the process or milestone entity.

答案2

得分: 0

你可以尝试在保存后添加 flush 方法。

英文:

You can try add flush method after save.

huangapple
  • 本文由 发表于 2020年7月31日 20:26:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/63191817.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定