使用 "pure" Hibernate/JPA 一次更新唯一列中的多个条目

Updating multiple entries in a unique column at once with "pure" Hibernate/JPA

我有一个 table 存储任务,还有一个 table 存储该任务的有序步骤。步骤属于任务父级(因此任务与步骤具有一对多关系)并且每个步骤都有一个 "ordinal" - 它是哪个步骤编号。我希望序号和父任务 ID 是一个组合的唯一约束,因此例如单个任务不能以两个不同的步骤设置为 "step 1",但不同的任务每个都可以有自己的 "step 1"。

我希望能够同时更新属于任务的每个步骤的序号,这样我就不会违反唯一约束。因此,如果我有 "Step 1," "Step 2" 和 "Step 3",我可以在 "Step 2" 处插入一个新步骤并将现有步骤移动到 "Step 3" 和 "Step 4." 我知道某些数据库允许您延迟检查约束,直到事务提交,但我非常希望我能避免任何需要某种数据库类型的事情,并且完全在 JPA/Hibernate 内完成。

这是我的实体的简化版本:

任务:

@Table
@Entity
public class Task {

    @Version
    private Long version;

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

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<Step> steps;
}

步骤:

@Entity
@Table(
    uniqueConstraints = {
        @UniqueConstraint(
            name = "UX_Step_Parent_Ordinal",
            columnNames = {"parent_id", "ordinal"})
    }
)
public class Step {

    @Version
    private Long version;

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

    @Column(nullable = false, name = "ordinal")
    private Long ordinal;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "parent_id")
    private Task parent;
}

到目前为止,我想到的唯一方法是首先在末尾插入新步骤,然后动态生成如下所示的 HQL 查询,它更新特定任务中的每个步骤使用案例一次:

UPDATE Step s
SET s.ordinal = CASE
 WHEN (s.id = 55) THEN 1
 WHEN (s.id = 58) THEN 2
 WHEN (s.id = 47) THEN 3
 WHEN (s.id = 33) THEN 4
 ... etc
 ELSE 0 END
WHERE s.parent.id = 5

但是我很确定这需要引擎在每次发生查询时重新处理查询,因此无法利用查询缓存或其他加速等功能。 Hibernate/JPA 还有其他方法吗?

不确定它是否真的解决了问题,但我认为您应该将集合建模为地图:

@OneToMany
@MapKeyJoinColumn(Name="ordinal")
private Map<Long,Step> steps;

当您在中间某处添加一个步骤时,您有责任更新整个地图。

如果您仍然需要步骤中的序号,则必须将其设置为只读:

@Column(nullable = false, name = "ordinal", insertable=false, updateable=false)
private Long ordinal;