使用“纯”Hibernate / JPA

时间:2017-07-05 13:43:42

标签: java hibernate jpa hql

我有一个存储任务的表,以及一个存储该任务的有序步骤的表。步骤属于任务父级(因此任务与步骤具有一对多的关系),并且每个步骤都有一个“序数” - 它是步骤编号。我希望序数和父任务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查询,该查询一次更新特定任务中的每一步案例:

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吗?

1 个答案:

答案 0 :(得分:1)

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

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

当您在其间的某处添加Step时,您有责任更新整个地图。

如果您仍然需要步骤中的序数,那么您必须将其设为readOnly:

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