Nano Hash - криптовалюты, майнинг, программирование

Удаление каскада JPA/Hibernate не работает

У меня есть эти сущности:

@Entity
public class Item extends Unit
{
    // @Id is in superclass

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<ItemRelation> lowerItemRelations = new LinkedHashSet<>();

    @OneToMany(mappedBy = "child", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<ItemRelation> higherItemRelations = new LinkedHashSet<>();

    // this too is in superclass
    @OneToMany(mappedBy = "unit", cascade = CascadeType.REMOVE, orphanRemoval = true)
    @OrderBy("date")
    protected Set<UnitRegistration> registrations = new LinkedHashSet<>();

    ...
}

@Entity
@Table(name = "ITEM_RELATION",
    indexes = @Index(columnList = "PARENT_ID, CHILD_ID", unique = true))
public class ItemRelation extends AbstractEntity
{
    // @Id is in superclass

    @ManyToOne(optional = false)
    @JoinColumn(name = "PARENT_ID")
    private Item parent;

    @ManyToOne(optional = false)
    @JoinColumn(name = "CHILD_ID")
    private Item child;

    @NotNull
    @Min(0)
    @Column(nullable = false, columnDefinition = "INT DEFAULT 1 NOT NULL")
    private int quantity = 1;

    ...
}

Теперь я просто хочу выполнить простой em.remove(item), но Hibernate не выдает соответствующие операторы DELETE для lowerItemRelations/higherItemRelations.

И наоборот, для всех других полей, аннотированных @OneToMany(mappedBy = "...", cascade = CascadeType.ALL/REMOVE, orphanRemoval=true), он выдает операторы.

Вот небольшой фрагмент журнала MySQL:

2016-09-28T08:47:52.090453Z   13 Query  update UNIT set CODE='CE13000003167', ... where ID=132241 and version=1
2016-09-28T08:47:52.094971Z   13 Query  delete from UNIT_ACTION where PARENT_ID=132241
2016-09-28T08:47:52.134999Z   13 Query  update AUTHORIZATION set UNIT_ID=null where UNIT_ID=132241
2016-09-28T08:47:52.158014Z   13 Query  delete from UNIT_DOCUMENT where PARENT_ID=132241
2016-09-28T08:47:52.248074Z   13 Query  delete from UNIT_PRODUCT where UNIT_ID=132241
2016-09-28T08:47:52.315641Z   13 Query  delete from UNIT_PROJECT where UNIT_ID=132241
2016-09-28T08:47:52.586008Z   13 Query  delete from ITEM_ALTERNATIVE where ITEM_ID=132241
2016-09-28T08:47:52.853350Z   13 Query  delete from AUTHORIZATION where ID=714491
2016-09-28T08:47:52.910835Z   13 Query  delete from UNIT_REGISTRATION where ID=173505
2016-09-28T08:47:52.980887Z   13 Query  delete from UNIT where ID=132241 and version=1
2016-09-28T08:47:53.133290Z   13 Query  rollback

Как видите, строки для удаления из ITEM_RELATION нет, и я ожидаю что-то вроде:

0000-00-00T00:00:00.000000Z   13 Query  delete from ITEM_RELATION where PARENT_ID=132241
0000-00-00T00:00:00.000000Z   13 Query  delete from ITEM_RELATION where CHILD_ID=132241

Очевидно, транзакция откатывается из-за:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`edea2`.`item_relation`, CONSTRAINT `FK_ITEM_RELATION_CHILD_ID` FOREIGN KEY (`CHILD_ID`) REFERENCES `unit` (`ID`)) 

Еще одна странность заключается в том, что Hibernate выполняет (ненужное?) UPDATE в качестве первого оператора.

Однако,

  • связано ли это другое поведение с тем фактом, что lowerItemRelations/higherItemRelations ссылается на один и тот же тип объекта (хотя и в разных полях/столбцах И разных строках)?

  • Это баг или есть причина такого поведения?

Что я пробовал:

  • инициализировать коллекции
  • инициализировать и очистить коллекции (для запуска orphanRemoval)
  • em.remove() каждый элемент коллекции до em.remove(item)

безуспешно.

Единственный рабочий способ, который я нашел до сих пор, - это выдать:

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaDelete<ItemRelation> delete = builder.createCriteriaDelete(ItemRelation.class);
Root<ItemRelation> rel = delete.from(ItemRelation.class);
delete.where(builder.or(
    builder.equal(rel.get(ItemRelation_.parent), managedItem),
    builder.equal(rel.get(ItemRelation_.child), managedItem)));

em.flush();

em.remove(managedItem);

Я использую Hibernate 5.2.2.Final на Wildfly 10.1.0.Final.

Спасибо


В соответствии с запросом здесь вызывается em.remove():

@Stateless
@Local
public class PersistenceService implements Serializable
{       
    @PersistenceContext
    private EntityManager em;

    ...

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public <T> void delete(T entity)
    {
        T managed;

        if(!em.contains(entity))
        {
            Class<T> entityClass = EntityUtils.getClass(entity);
            Object entityId = EntityUtils.getIdentifier(entity);

            managed = em.find(entityClass, entityId);
        }
        else
        { 
            managed = entity;
        }

        em.remove(managed);

        // em.flush(); // just for debugging
    }
}

  • Не могли бы вы добавить фрагмент кода, где em.remove(item) терпит неудачу? 28.09.2016
  • Пожалуйста, смотрите обновление. Ошибка при фиксации tx (или em.flush() при раскомментировании) 28.09.2016

Ответы:


1

Хорошо, это ошибка.

Я обнаружил, что это поведение связано с ленивой инициализацией обеих коллекций, поэтому я отправил сообщение о проблеме HHH-11144 и создал простой тестовый пример (также доступен на GitHub).

Короче говоря, это происходит, когда

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();

tx.begin();

Item item = em.createQuery("select x from Item x where x.code = 'first'", Item.class).getSingleResult();

Set<ItemRelation> lowerItemRelations = item.getLowerItemRelations();
Hibernate.initialize(lowerItemRelations);

// initializing 'higherItemRelations' prevents orphanRemoval to work on 'lowerItemRelations'
Set<ItemRelation> higherItemRelations = item.getHigherItemRelations();
Hibernate.initialize(higherItemRelations);

lowerItemRelations.clear();

tx.commit();
em.close();
05.10.2016

2

Могу поспорить, что Unit ссылается на что-то еще и не является эксклюзивным для вашего корня - ItemRelation в этом случае не может быть удален.

Ваше сопоставление предполагает, что Unit может иметь несколько ItemRelations, так что, скорее всего, проблема здесь. Сначала вам придется «разорвать» отношение, удалив взаимные ссылки с обеих сторон (или, по крайней мере, на объект, который содержит FK, или любой из них, если используется таблица соединений)

28.09.2016
  • Я не понимаю вашего ответа... Конечно, Unit ссылается на другие объекты (например, UnitRegistrations, которые я привел в пример), на самом деле цель состоит в том, чтобы удалить их все сразу (где они каскадированы). Сопоставление между Item ‹-› ItemRelation полностью выражено во фрагменте (@OneToMany/@ManyToOne с @JoinColumn), вы видите, что @JoinTable нет. Пожалуйста, можно поподробнее? 28.09.2016
  • Я знаю, что действие может каскадироваться сверху вниз, где оно заканчивается, но вы хотите, чтобы каскадный процесс возвращался снизу вверх и начинался снова с N числом других связанных объектов. ИМХО cascade с этим не справится, но могу ошибаться. 28.09.2016
  • Нет, ты ошибаешься. Я не хочу удалять связанные элементы, я просто хочу удалить managedItem и все его lowerItemRelations и higherItemRelations Объекты ItemRelation, которые содержат два FK для таблицы UNIT - как вы можете видеть, ItemRelation.parent и ItemRelation.child не объявляют атрибут каскада, поэтому я не прошу hibernate удалить полный граф, только целевой объект и непосредственные соседи ItemRelation (где объявлен каскад) :) 28.09.2016
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..


    © 2024 nano-hash.ru, Nano Hash - криптовалюты, майнинг, программирование