Hibernate:不删除父删除时的子集合

时间:2017-02-08 16:42:33

标签: mysql spring hibernate

我有一个让你咆哮的奇怪问题。我有(也很奇怪)Spring / Hibernate应用程序,它旨在以下列方式管理数据库(我简化了一些事情,所以不要混淆源代码提到略有不同的表/列):

active_proxy_view table:
id     | entity
<uuid> | <string containing json>

archive_proxy_view table:
id     | entity
<uuid> | <string containing json>

track_reference table:
ref_type | ref_id | track_domain  | track_type | track_id |
'proxy'  | <uuid> | 'example.com' | 'customer' | '123'    |

保留两个表是必需的 - 我需要同时拥有历史记录/统计查询和业务值查询,以便现在处于活动状态,因此我需要为活动代理保持紧密设置。 track_reference表用于搜索,因此我可以执行以下查询:

SELECT p.id, p.entity FROM archive_proxy_view AS p
INNER JOIN track_reference AS t1 ON 
  t1.ref_id = p.id AND 
  t1.ref_type = 'proxy' AND
  t1.track_domain = 'example.com' AND
  t1.track_type = 'customer' AND
  t1.track_id = '123'
INNER JOIN track_reference AS t2 ON 
  t2.ref_id = p.id AND 
  t2.ref_type = 'proxy' AND
  t2.track_domain = 'example.com' AND
  t2.track_type = 'campaign' AND
  t2.track_id = 'halloween-2017'

(可能不是100%正确,我有一段时间没有原始的SQL经验)

这就是问题所在:

  • active_proxy_viewarchive_proxy_view个实体都从一个在@OneToMany实体上指定track_reference关系的类继承; @ManyToOne使用率实际上是不可能的,因为有很多实体与跟踪参考相关联
  • track_reference是单独管理的(这也是强制性的)
  • 我需要与track_reference表分开管理视图,但每当我告诉Hibernate从active_proxy_view表中删除实体时,它也会带走track_reference个实体。即使我使用级联注释值,默认情况下为空(并且据我所知,这意味着不应该使用父级删除子记录)。但是,我有可能错过了一些东西。
  • 我也没有使用自定义@SQLDeleteAll破解整个事情,我仍然可以在常规日志中看到常规删除:

    55 Query     delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='agent'
    55 Query     delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='lead'
    55 Query     delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='source'
    53 Query     DELETE FROM `tracking_reference` WHERE `referenced_entity_type` = 'proxy' AND referenced_entity_id = '13c6b55c-f9b7-4de7-8bd4-958d487e461c' AND 1 = 0
    
  • 我正在使用Hibernate 5.2.3.Final通过Spring 4.3.2.RELEASE / Spring Data JPA 1.10.2.RELEASE

TL; DR

所以,问题是:如何在删除父级时阻止Hibernate删除关联实体?

实体的源代码如下所示:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class ProxyViewEntryTemplate {

    @Id
    @NotNull
    @Column(nullable = false)
    private String id;

    @NotNull
    @Column
    private String entity;

    // some other columns

    @OneToMany
    @JoinColumn(name = TrackRef.REFERENCE_ID_COLUMN_NAME) // 'reference_entity_id`
    @Where(clause = ProxyView.TRACK_WHERE_JOIN_CLAUSE) // `referenced_entity_type` = 'proxy'
    @SQLDeleteAll(sql = ProxyView.TRACK_DELETE_ALL_QUERY) // DELETE FROM `tracking_reference` WHERE `referenced_entity_type` = 'proxy' AND referenced_entity_id = ? AND 1 = 0
    private Collection<TrackingReference> track = new ArrayList<>();

    // setters, getters, hashCode, equals
}

@Entity
@Table(name = "active_proxy")
public class ActiveProxyViewEntry extends ProxyViewEntryTemplate {}


@Entity
@Table(name = "tracking_reference")
@IdClass(TrackingReferenceId.class)
public class TrackingReference {

    @Id
    @Column(name = "tracked_entity_type", nullable = false)
    @NotNull
    private String trackedType;

    @Id
    @Column(name = "tracked_entity_domain", nullable = false)
    private String trackedDomain;

    @Id
    @Column(name = "tracked_entity_id", nullable = false)
    private String trackedId;

    @Id
    @Column(name = "referenced_entity_type", nullable = false)
    @NotNull
    private String referencedType;

    @Id
    @Column(name = "referenced_entity_id", nullable = false)
    @NotNull
    private String referencedId;

    // setters, getters, hashCode, equals
}

整个事情是通过Spring JPA Repositories管理的:

@NoRepositoryBean
public interface SuperRepository<E, ID extends Serializable> extends PagingAndSortingRepository<E, ID>,
        JpaSpecificationExecutor<E> {
}

public interface ActiveProxyViewRepository extends SuperRepository<ActiveProxyViewEntry, String> {}

// the call for deletion
public CompletableFuture<Void> delete(ID id) {
    ...
    descriptor.getRepository().delete(descriptor.getIdentifierConverter().convert(id));
    ...
}
// which is equal to
...
ActiveProxyViewRepository repository = descriptor.getRepository();
String uuidAsString = descriptor.getIdentifierConverter().convert(id);
repository.delete(uuidAsString);
...

2 个答案:

答案 0 :(得分:0)

如果删除@JoinColumn,则不应出现此问题。

如果需要保留@JoinColumn,则需要删除持久性提供程序通过将注释更改为自动应用的外键约束要求:

@JoinColumn(
  name = "yourName"
  foreignKey = @Foreignkey(value = ConstraintMode.NO_CONSTRAINT)
)

然后,您应该能够删除视图实体,而不必强制删除跟踪引用。

答案 1 :(得分:0)

事实证明这是一个典型的拍摄自己的脚步场景。

跟踪引用以相当复杂的方式更新:

  • 构建要存储在数据库(C1)中的引用集合
  • 加载所有现有参考文献(C2)
  • 商店C1
  • 删除C2中存在但未在C1中引用的所有引用(使用collection.removeAll)

事实证明我的.equals方法写错了,几乎在每种情况下都返回false。因此,通常每个引用都从数据库中删除(您可以在相关日志中看到的查询),所以这是我的错。

在我修复之后,只运行@SQLDeleteAll查询 - 由于我不知道的原因,它仍然像是设置了cascade选项一样。我设法使用@OneToMany(updatable = false, insertable = false)摆脱它;它似乎是一个肮脏的黑客,但我没有足够的时间来挖掘它。

我还没有彻底测试过,但是,我希望,这可以解决问题。