【问题标题】:Hibernate @OneToMany reference child not updated / lost?Hibernate @OneToMany 参考子没有更新/丢失?
【发布时间】:2020-07-31 12:26:45
【问题描述】:

假设我有三个实体。

@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;

}

我想从我的客户那里将 ResultDefinition 类型的对象添加到进程中的里程碑,如下所示:

@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);
}

在我的方法 createResult 中,我将 resultDefinition 添加到里程碑结果集合中。 当我保存父 foundProcess 时,我看到 foundprocess->milestone->resultDefinition 得到了持久化并获得了一个 ID。当我调用 resultDefinition.getId() 它返回 null。 foundProcess 中的 ResultDefinition 对象也是另一个引用,与我添加到milestone.results 中的不同。

为什么我在调用milestone.getResult() 时会得到正确的实例?


编辑:我的 processService / repository 的实现

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

public interface ProcessRepository extends JpaRepository<Process, Long>, JpaSpecificationExecutor<Process> {
        ...
    }

【问题讨论】:

    标签: java hibernate spring-data-jpa


    【解决方案1】:

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

    在您的情况下,您正在保存父进程。所以你必须通过流程或里程碑实体来访问保存的 ResultDefinition。

    【讨论】:

    • 谢谢。我猜这个里程碑是从托管流程中得到的,所以它是托管的?
    • 是的,我想是的。这取决于您在 getmilestone 方法中所做的事情。但是你的 createResult 方法在 @Transactional 中运行,我猜它仍然是托管的。
    • 因此,您是说在这种情况下,除了查看托管父进程或里程碑之外,没有其他方法可以更新引用。
    • 可能还有其他方法,例如ResultDefinition 具有唯一的业务密钥,您可以通过 findByXY 从会话中获取它,但在此设置中,我认为最简单的方法是通过其父项访问它。
    • 谢谢,您是否可以看看与此相关的另一个问题? stackoverflow.com/questions/63192842/…
    【解决方案2】:

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

    【讨论】:

    • 保存时会刷新。我正在使用 JpaRepository::saveAndFlush (见编辑)
    猜你喜欢
    • 1970-01-01
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 2012-03-20
    • 1970-01-01
    • 1970-01-01
    • 2016-11-19
    • 2011-05-04
    相关资源
    最近更新 更多