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



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

我希望能够同时更新属于任务的每个步骤的序号,这样我就不会违反唯一约束。因此,如果我有"步骤 1"、"步骤 2"和"步骤 3",我可以在"步骤 2"插入一个新步骤,并将现有步骤移动到"步骤 3"和"步骤 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 查询,该查询使用 CASE 一次更新特定任务中的每个步骤:

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;

最新更新