【问题标题】:How can I cascade delete a collection which is part of a JPA entity?如何级联删除属于 JPA 实体的集合?
【发布时间】:2011-12-03 11:52:26
【问题描述】:
@Entity
public class Report extends Model {

    public Date date;
    public double availability;

    @ElementCollection
    @Cascade(value={CascadeType.ALL})
    public Map<FaultCategory, Integer> categories;      
}

在我的一项工作中,我有以下代码:

int n = MonthlyReport.delete("date = ?", date);

这总是无法删除实体并出现以下错误:

DELETE 语句与 REFERENCE 约束“FK966F0D9A66DB1E54”冲突。冲突发生在数据库“TFADB”、表“dbo.MonthlyReport_categories”、列“MonthlyReport_id”中。

如何指定映射,以便在删除报告时删除 categories 集合中的元素?

【问题讨论】:

  • JPA 不支持对元素集合进行级联删除(另请参阅 here

标签: java hibernate jpa collections playframework


【解决方案1】:

级联删除(以及一般的级联操作)仅在通过EntityManager 完成操作时有效。不是通过 JP QL /HQL 查询作为批量删除完成删除时。当通过查询完成删除时,您不能指定将删除链接到 ElementCollection 中的元素的映射。

ElementCollection 注解没有级联属性,因为操作总是级联的。当您通过EntityManager.remove() 删除实体时,操作将级联到ElementCollection

您必须获取所有要删除的 MonthlyReport 实体并为每个实体调用 EntityManager.remove。在 Play 框架中,您也可以在实体中调用 delete-method,而不是这个。

【讨论】:

  • 那么删除类别的正确方法是什么?我注意到,如果我将外键设置为对数据库本身进行级联,则 MonthyReport.delete 实际上会成功。
  • 添加到答案中,只需使用您的日期获取它们=? -标准并一一删除/删除。然后删除将级联到类别。
  • 操作没有级联怎么办?
【解决方案2】:

我们找到了神奇的门票!将 OnDelete(action= OnDeleteAction.CASCADE) 添加到 ElementCollection。这允许我们从 SQL 中删除该项目(在 entityManager 之外)。

【讨论】:

  • org.hibernate.MappingException: 只有反向一对多关联可以使用 on-delete="cascade"
  • org.hibernate.AnnotationException:用@OnDelete 注释的单向一对多关联必须定义@JoinColumn。额外的@JoinColumn 有帮助,见上面的答案
【解决方案3】:

J.T. 提供的答案。是正确的,但对我和 sebge2 来说是不完整的,正如他/她的评论中所指出的那样。 @ElementCollection@OnDelete 的组合还需要@JoinColumn()

后续示例:

@Entity
public class Report extends Model {
    @Id
    @Column(name = "report_id", columnDefinition = "BINARY(16)")
    public UUID id; // Added for the sake of this entity having a primary key
    public Date date;
    public double availability;

    @ElementCollection
    @CollectionTable(name = "report_category", joinColumns = @JoinColumn(name = "report_id")) // choose the name of the DB table storing the Map<>
    @MapKeyColumn(name = "fault_category_key") // choose the name of the DB column used to store the Map<> key
    @Column(name = "fault_category_value")     // choose the name of the DB column used to store the Map<> value
    @JoinColumn(name = "report_id")            // name of the @Id column of this entity
    @OnDelete(action = OnDeleteAction.CASCADE)
    @Cascade(value={CascadeType.ALL})
    public Map<FaultCategory, Integer> categories;      
}

此设置将创建一个名为 report 的表和另一个表 report_category,其中包含三列:report_id, fault_category_key, fault_category_valuereport_category.report_idreport.report_id 之间的外键约束将是 ON DELETE CASCADE。我用 Map 测试了这个设置。

【讨论】:

  • 那么你如何删除元素集合?我使用@Query 注释和 JPQL 来删除父实体。但是,子元素集合不会被删除。
  • @RocasYeh 从技术上讲,这两种方案都应该按您的预期工作:方案 1) 您使用 CrudRepositorydelete() 函数删除父实体,这意味着 JPA 会为您删除父条目和所有子条目。或者场景 2)您使用带有 @Query 的本机 SQL 并为父实体行执行 DELETE,RDBMS 会自动删除所有子实体,因为 ON DELETE CASCADE 设置。如果您无法隔离问题,请随时提出单独的问题并标记我。
  • 这是一个了不起的答案 +1,根据我自己的测试,我至少需要 @JoinColumn@OnDelete 才能让它工作。
【解决方案4】:

我遇到了同样的问题,这是我的代码示例。

@ElementCollection
@CollectionTable(name = "table_tag", joinColumns=@JoinColumn(name = "data_id"))
@MapKeyColumn(name = "name")
@Column(name = "content")
private Map<String, String> tags

经过多次尝试,最后,我只是将table_tag.data_id的外键约束添加到父表的主键中。请注意,您应该将 ON DELETE CASCADE 设置为约束。 可以通过任何方式删除父实体,子元素集合也会被删除。

【讨论】:

    【解决方案5】:

    作为hibernate特定注解@org.hibernate.annotations.OnDelete的替代方案,您还可以通过@javax.persistence.ForeignKey提供约束来自定义自动生成模式:

    @CollectionTable(name = "foo_bar", foreignKey = @ForeignKey(
            name = "fk_foo_bar", 
            foreignKeyDefinition = "foreign key (foo_id) references Foo (id) on delete cascade"))
    private List<String> bar;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-22
      • 2013-03-23
      • 1970-01-01
      • 2011-01-29
      相关资源
      最近更新 更多