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

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

Hibernate @OneToMany reference child not updated / lost?

问题

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

Say I have three entities. 

@Entity
public class Process {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String name;

    @ManyToAny(
            metaColumn = @Column(name = "node_type"),
            fetch = FetchType.LAZY
    )
    @AnyMetaDef(
            idType = "long", metaType = "string",
            metaValues = {
                    @MetaValue(targetEntity = Milestone.class, value = MILESTONE_DISC),
                    @MetaValue(targetEntity = Phase.class, value = PHASE_DISC)
            }
    )
    @Cascade({org.hibernate.annotations.CascadeType.ALL})
    @JoinTable(
            name = "process_nodes",
            joinColumns = @JoinColumn(name = "process_id", nullable = false),
            inverseJoinColumns = @JoinColumn(name = "node_id", nullable = false)
    )
    private Collection<ProcessNode> nodes = new ArrayList<>();

    ...
}

@Entity
@ToString
@DiscriminatorValue(MILESTONE_DISC)
public class Milestone implements ProcessNode {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Collection<ResultDefinition> results;

    @ManyToOne()
    private Process process;

...
}

@Entity
@ToString
public class ResultDefinition {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String externalId;
    private String name;
    private ResultType resultType;

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

@Transactional
@PostMapping("/{milestone_id}/results")
public ResultDefinitionDto createResult(@PathVariable("milestone_id") Long milestoneId, @RequestBody ResultDefinitionDto dto) {
    Process foundProcess = getProcess(milestoneId);
    checkFoundProcess(milestoneId, foundProcess);
    Milestone milestone = getMilestone(foundProcess, milestoneId);
    ResultDefinition resultDefinition = resultDefinitionMapper.fromDTO(dto);
    milestone.addResult(resultDefinition);
    processService.save(foundProcess);
    //TODO: Find out why this is necessary (???)
    ResultDefinition savedResult = milestone.getResult(resultDefinition.getName());
    return resultDefinitionMapper.fromEntity(savedResult);
}
In my method createResult I add resultDefinition to the milestone results collection.
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. 

Why do I get the correct instance when calling milestone.getResult()?
Edit: my implementation of processService / repository

@Override
public Process save(Process entity) {
    return processRepository.saveAndFlush(entity);
}
public interface ProcessRepository extends JpaRepository<Process, Long>, JpaSpecificationExecutor<Process> {
    ...
}
英文:

Say I have three entities.

@Entity
public class Process {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String name;
@ManyToAny(
metaColumn = @Column(name = &quot;node_type&quot;),
fetch = FetchType.LAZY
)
@AnyMetaDef(
idType = &quot;long&quot;, metaType = &quot;string&quot;,
metaValues = {
@MetaValue(targetEntity = Milestone.class, value = MILESTONE_DISC),
@MetaValue(targetEntity = Phase.class, value = PHASE_DISC)
}
)
@Cascade({org.hibernate.annotations.CascadeType.ALL})
@JoinTable(
name = &quot;process_nodes&quot;,
joinColumns = @JoinColumn(name = &quot;process_id&quot;, nullable = false),
inverseJoinColumns = @JoinColumn(name = &quot;node_id&quot;, nullable = false)
)
private Collection&lt;ProcessNode&gt; nodes = new ArrayList&lt;&gt;();
...
}

@Entity
@ToString
@DiscriminatorValue(MILESTONE_DISC)
public class Milestone implements ProcessNode {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection&lt;ResultDefinition&gt; results;
@ManyToOne()
private Process process;
...
}

@Entity
@ToString
public class ResultDefinition {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String externalId;
private String name;
private ResultType resultType;
}

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

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

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

@Override
public Process save(Process entity) {
return processRepository.saveAndFlush(entity);
}

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

答案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:

确定