Hibernate – @ElementCollection – странное поведение удаления / вставки

@Entity public class Person { @ElementCollection @CollectionTable(name = "PERSON_LOCATIONS", joinColumns = @JoinColumn(name = "PERSON_ID")) private List locations; [...] } @Embeddable public class Location { [...] } 

Учитывая следующую структуру classов, когда я пытаюсь добавить новое место в список локаций Person, он всегда приводит к следующим SQL-запросам:

 DELETE FROM PERSON_LOCATIONS WHERE PERSON_ID = :idOfPerson 

А также

 A lotsa' inserts into the PERSON_LOCATIONS table 

Hibernate (3.5.x / JPA 2) удаляет все связанные записи для данного Person и повторно вставляет все предыдущие записи, а также новый.

У меня возникла идея, что метод equals / hashcode на Location решит проблему, но ничего не изменит.

Любые намеки приветствуются!

Проблема как-то объясняется на странице об ElementCollection JPA wikibook:

Первичные ключи в CollectionTable

Спецификация JPA 2.0 не дает возможности определить Id в Embeddable . Однако для удаления или обновления элемента отображения ElementCollection обычно требуется уникальный ключ. В противном случае при каждом обновлении провайдеру JPA необходимо будет удалить все из CollectionTable для Entity , а затем вставить значения обратно. Таким образом, поставщик JPA, скорее всего, предположит, что комбинация всех полей в Embeddable уникальна в сочетании с внешним ключом ( JoinColunm (s)). Однако это может быть неэффективным или просто невозможным, если Embeddable большая или сложная.

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

Однако, если вы определяете @OrderColumn (чтобы указать столбец, используемый для поддержания постоянного порядка списка, что имеет смысл с тех пор, как вы используете List ), Hibernate создаст первичный ключ (сделанный из столбца порядка и join ) и сможет обновить таблицу коллекций без удаления всего содержимого.

Что-то вроде этого (если вы хотите использовать имя столбца по умолчанию):

 @Entity public class Person { ... @ElementCollection @CollectionTable(name = "PERSON_LOCATIONS", joinColumns = @JoinColumn(name = "PERSON_ID")) @OrderColumn private List locations; ... } 

Рекомендации

  • Спецификация JPA 2.0
    • Раздел 11.1.12 «Аннотация элемента ElementCollection»
    • Раздел 11.1.39 «Кодовое обозначение заказа»
  • JPA Wikibook
    • Сохранение Java / ElementCollection

В дополнение к ответу Паскаля вы также должны установить по крайней мере один столбец как NOT NULL :

 @Embeddable public class Location { @Column(name = "path", nullable = false) private String path; @Column(name = "parent", nullable = false) private String parent; public Location() { } public Location(String path, String parent) { this.path = path; this.parent= parent; } public String getPath() { return path; } public String getParent() { return parent; } } 

Это требование описано в AbstractPersistentCollection :

Обходной путь для таких ситуаций, как HHH-7072. Если элемент коллекции является компонентом, который целиком состоит из свойств с нулевым значением, мы в настоящее время должны принудительно воссоздать всю коллекцию. Дополнительную информацию см. В использовании hasNotNullableColumns в конструкторе AbstractCollectionPersister. Чтобы удалить строку за строкой, для SQL потребуется «WHERE (COL =? OR (COL имеет значение null AND? Равно null))», а не текущий «WHERE COL =?» (сбой для null для большинства БД). Обратите внимание, что параметр должен быть связан дважды. До тех пор, пока мы в конце концов не добавим понятия «привязки параметров» к AST в ORM 5+, обработка этого типа условий является либо чрезвычайно сложной, либо невозможной. Форсирование restа не является идеальным, но на самом деле не имеет другого варианта в ORM 4.

  • В чем разница между fetch = "EAGER" и fetch = "LAZY" в доктрине
  • .net ORM Сравнение
  • Entity Framework Code First - два внешних ключа из одной таблицы
  • Перечисление карты в JPA с фиксированными значениями?
  • Ошибка: java.lang.NoSuchMethodError: org.objectweb.asm.ClassWriter. (I) V
  • Postgresql UUID поддерживается Hibernate?
  • Какова лучшая страtagsя для модульных приложений, управляемых базами данных?
  • Родные запросы JPA / Hibernate не распознают параметры
  • Какова проблема с запросом SELECT N + 1?
  • session.connection () не рекомендуется в Hibernate?
  • Утечка памяти Symfony2 Doctrine2 / превышение предела памяти
  • Давайте будем гением компьютера.