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 и повторно вставляет все предыдущие записи, а также новый.
- Как создать таблицу объединений с аннотациями JPA?
- Как использовать Hibernate @ Любые связанные annotations?
- Я обнаружил, что JPA, или, похоже, не поощряет шаблон DAO
- Hibernate, iBatis, Java EE или другой инструмент Java ORM
- Перечисление в спящем режиме, сохраняющееся как перечисление
У меня возникла идея, что метод equals / hashcode на Location решит проблему, но ничего не изменит.
Любые намеки приветствуются!
- Когда и почему сущности JPA должны реализовывать интерфейс Serializable?
- Entity Framework 4 vs NHibernate
- JPQL Создать новый объект в Select Statement - избегать или объявлять?
- Как ускорить объединение нагрузки без дублирования в NHibernate?
- Как обновить схемы таблиц базы данных с помощью генерации схемы NHibernate?
- Сохранение карты с использованием JPA
- Entity Framework .Remove () vs. .DeleteObject ()
- Бесконечная recursion с выпуском Jackson JSON и Hibernate JPA
Проблема как-то объясняется на странице об 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.