Hibernate删除不按顺序发生

时间:2017-06-06 23:24:37

标签: java hibernate jpa

我有以下课程:

分类

@Entity
@Table(name = "category", schema = "customer")
public class Category extends CustomerOwned {
   ...
}

CatalogueItem

@Entity
@Table(name = "catalogue_item", schema = "customer")
@EntityListeners(value = { CatalogueItemStatusListener.class })
public class CatalogueItem extends CatalogueOwned implements Statusable<CatalogueItem, CatalogueItemStatus> {
    ...
    @OneToMany(mappedBy = "catalogueItem", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<CatalogueItemCategory> categories;
    ...
}

CatalogueItemCategory

@Entity
@Table(name = "catalogue_item_category", schema = "customer")
public class CatalogueItemCategory extends CatalogueItemOwned implements CategoryOwner {
    ...
    @ManyToOne
    @JoinColumn(name = "category_id", nullable = false, insertable = true, updatable = false)
    private Category category;
    ...
}

我们在应用程序中执行的删除次数非常少,但类别是很难删除的。 CatalogueItemCategory 也被硬删除。

我正在创建一项服务,允许某人删除一个类别,同时检查所有类别依赖项并删除它们,所以我这样做:

@Transactional
public DependentItemsResultResource delete(String categoryId, boolean force) {
    List<CatalogueItem> catalogueItems = catalogueItemRepository.getByCategory(getCustomerStringId(), categoryId);

    DependentItemsResultResource resultResource = new DependentItemsResultResource();
    resultResource.setDependentCatalogueItems(new SearchResponse().from(catalogueItems, CatalogueItemSummaryResource::new));

    if (!catalogueItems.isEmpty()) {
        if (!force) {
            resultResource.setDeleted(false);
            return resultResource;
        }

        catalogueItems.forEach(item -> {
            item.getCategories().removeIf(catalogueItemCategory -> categoryId.equals(catalogueItemCategory.getCategory().getStringId()));
            sendEvent(prepareEvent(new CatalogueItemUpdatedEvent(), item.getCatalogue().getStringId(), item.getStringId()));
        });
    }

    Category category = getCategory(categoryId);
    resultResource.setDeleted(true);
    categoryRepository.delete(category);

    CategoryDeletedEvent event = new CategoryDeletedEvent();
    event.setCategoryPath(category.getPath());
    sendEvent(prepareEvent(event, categoryId));
    return resultResource;
}

这导致以下异常:

Caused by: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "FKLYKUEHJG9UL0LR36O5XDD3MLF: CUSTOMER.CATALOGUE_ITEM_CATEGORY FOREIGN KEY(CATEGORY_ID) REFERENCES CUSTOMER.CATEGORY(ID) (1)"; SQL statement:
delete from customer.category where id=? [23503-192]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    at org.h2.message.DbException.get(DbException.java:179)
    at org.h2.message.DbException.get(DbException.java:155)
    at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:426)
    at org.h2.constraint.ConstraintReferential.checkRowRefTable(ConstraintReferential.java:443)
    at org.h2.constraint.ConstraintReferential.checkRow(ConstraintReferential.java:318)
    at org.h2.table.Table.fireConstraints(Table.java:967)
    at org.h2.table.Table.fireAfterRow(Table.java:985)
    at org.h2.command.dml.Delete.update(Delete.java:101)
    at org.h2.command.CommandContainer.update(CommandContainer.java:98)
    at org.h2.command.Command.executeUpdate(Command.java:258)
    at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:160)
    at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:146)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
    ... 109 common frames omitted

我试过更换这一位:

item.getCategories().removeIf(catalogueItemCategory -> categoryId.equals(catalogueItemCategory.getCategory().getStringId()));

用这个:

item.getCategories().stream()
    .filter(catalogueItemCategory -> categoryId.equals(catalogueItemCategory.getCategory().getStringId()))
    .forEach(catalogueItemCategoryRepository::delete);

但它没有任何区别。 Hibernate正在不按顺序删除对象。应该在类别对象之前删除CatalogueItemCategory对象,但事实并非如此。

如何以正确的顺序删除它?

编辑:错过了这个提供CatalogueItemCategory的父级的类:

@MappedSuperclass
public class CatalogueItemOwned extends Updateable {
    ...
    @ManyToOne
    @JoinColumn(name = "catalogue_item_id", nullable = false, insertable = true)
    private CatalogueItem catalogueItem;
    ...
}

2 个答案:

答案 0 :(得分:0)

有一些肮脏的解决方案,既有反模式,但也可能有效:

每次删除后使用flush,或使用本机查询删除。

尽管如此,我建议不要使用任何这些解决方案,而是寻找更清洁的解决方案。

答案 1 :(得分:0)

我通过显式删除类别和所有依赖项来解决这个问题,如下所示:

@Transactional
public DependentItemsResultResource delete(String categoryId, boolean force) {
    List<CatalogueItem> catalogueItems = catalogueItemRepository.getByCategory(getCustomerStringId(), categoryId);

    DependentItemsResultResource resultResource = new DependentItemsResultResource();
    resultResource.setDependentCatalogueItems(new SearchResponse().from(catalogueItems, CatalogueItemSummaryResource::new));

    if (!catalogueItems.isEmpty()) {
        if (!force) {
            resultResource.setDeleted(false);
            return resultResource;
        }

        catalogueItems.forEach(item -> sendEvent(prepareEvent(
            new CatalogueItemUpdatedEvent(), item.getCatalogue().getStringId(), item.getStringId())));
    }

    Category category = getCategory(categoryId);
    resultResource.setDeleted(true);
    categoryRepository.deleteCatalogueItemCategoriesByCategory(category);
    categoryRepository.deleteCategory(category);

    CategoryDeletedEvent event = new CategoryDeletedEvent();
    event.setCategoryPath(category.getPath());
    sendEvent(prepareEvent(event, categoryId));
    return resultResource;
}

回购方法是:

@Modifying
@Query("delete from Category c where c = :category")
void deleteCategory(@Param("category") Category category);

@Modifying
@Query("delete from CatalogueItemCategory where category = :category")
void deleteCatalogueItemCategoriesByCategory(@Param("category") Category category);

不是最好的,但也不是最差的。不使用本机删除,它仍在主要事务中。