Каскадное удаление коллекции встраиваемых объектов

У меня есть объект A с набором базовых типов (например, String). Я использую такое сопоставление, потому что строки, связанные с каждым экземпляром A, зависят от жизненного цикла A. Если я хочу удалить экземпляр A из БД, я также хочу, чтобы связанные с ним String были удалены.

Мое отображение выглядит следующим образом:

@Entity
public class A {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "name", nullable = false, unique = true)
    private String name;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "AStrings", joinColumns = @JoinColumn(name = "id"))
    @Column(name = "strings", nullable = false)
    private Set<String> strings;
}

Если я создам экземпляр A и добавлю в него несколько строк, я смогу сохранить экземпляр, используя Session.save(myInstance). И экземпляр A, и связанные с ним String сохраняются.

Но если я хочу удалить тот же экземпляр из БД, используя Session.createQuery("delete A a where a.name = ?").setString(0, name).executeUpdate(), я получаю ошибку ограничения внешнего ключа:

Невозможно удалить или обновить родительскую строку: ограничение внешнего ключа не работает

Но я ожидаю, что связанные String будут автоматически удалены перед удалением экземпляра A, но, похоже, это не так. Я также не нашел способа указать каскадные правила.

Что-то не так с моей конфигурацией?

Спасибо


EDIT: я также пытался использовать @Cascade(CascadeType.DELETE) в поле strings, и это все еще не помогает. Просматривая базу данных, я не вижу никакой политики ON DELETE для соответствующего внешнего ключа.

Кто-то, у кого была такая же проблема, открыл JIRA: https://hibernate.onjira.com/browse/HHH-4301. Решение (или обходной путь) должно существовать, я не могу быть единственным человеком, который использует @ElementCollection.

Я решил проблему. Я думал, что удаление с помощью Session.delete() или с помощью запроса HQL эквивалентно, но, похоже, это не так. Используя запрос HQL, зависимые объекты не удаляются автоматически, поэтому я получаю ошибку ограничения внешнего ключа. Использование Session.delete() решает проблему. Кроме того, Hibernate, похоже, не использует каскадные функции БД, поскольку я до сих пор не вижу никакой политики CASCADE в сгенерированном DDL, он обрабатывает это внутри.


person Mickael Marrache    schedule 29.08.2012    source источник


Ответы (4)


Я решил проблему.

Я думал, что удаление объекта с помощью Session.delete() или с помощью запроса HQL эквивалентно, но, похоже, это не так. Используя запрос HQL, зависимые объекты не удаляются автоматически, поэтому я получаю ошибку ограничения внешнего ключа, как объясняется в вопросе.

Использование Session.delete() решает проблему. Кроме того, Hibernate, похоже, не использует каскадные функции БД, поскольку я до сих пор не вижу никакой политики CASCADE в сгенерированном DDL, он обрабатывает это внутри.


Для модераторов:

Я уже добавлял свой ответ на вопрос в прошлом (как вы просили), но, поскольку это ответ, который решает проблему, и ответ (от других пользователей) не был опубликован, я думаю, что должен опубликовать его здесь как ответ .

person Mickael Marrache    schedule 15.01.2013
comment
Это действительно работает! Проблема в том, что использование HQL намного медленнее... - person BrunoJCM; 17.11.2016

  • как насчет добавления конфигурации orphanRemoval?

  • или вызвать A.strings().clear() вручную перед удалением одного экземпляра A?

если оба вышеперечисленных не работают, возможно, есть какая-то ошибка для ElementCollection

person donnior    schedule 29.08.2012

Тест принудительного удаления сироты:

@Cascade( { org.hibernate.annotations.CascadeType.ALL, 
    org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
person Manu Navarro    schedule 29.08.2012
comment
@Cascade предназначен для сущностей @OneToManys, а не для @ElementCollections. - person BrunoJCM; 16.11.2016

Добавьте «@Cascade» (org.hibernate.annotations.Cascade)

Ex:

@Entity
public class A {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "AStrings", joinColumns = @JoinColumn(name = "id"))
    @Column(name = "strings", nullable = false)
    @Cascade(value=org.hibernate.annotations.CascadeType.ALL)
    private Set<String> strings;
}
person Manu Navarro    schedule 29.08.2012
comment
@Cascade предназначен для сущностей @OneToManys, а не для @ElementCollections. - person BrunoJCM; 16.11.2016