Hibernate CollectionOfElements EAGER извлекает дубликаты элементов
У меня есть класс под названием SynonymMapping, который имеет набор значений, отображаемых как CollectionOfElements
@Entity(name = "synonymmapping")
public class SynonymMapping {
@Id private String keyId;
//@CollectionOfElements(fetch = FetchType.EAGER)
@CollectionOfElements
@JoinTable(name="synonymmappingvalues", joinColumns={@JoinColumn(name="keyId")})
@Column(name="value", nullable=false)
@Sort(type=SortType.NATURAL)
private SortedSet<String> values;
public SynonymMapping() {
values = new TreeSet<String>();
}
public SynonymMapping(String key, SortedSet<String> values) {
this();
this.keyId = key;
this.values = values;
}
public String getKeyId() {
return keyId;
}
public Set<String> getValues() {
return values;
}
}
У меня есть тест, где я храню два объекта SynonymMapping в базе данных и затем запрашиваю базу данных, чтобы вернуть все сохраненные объекты SynonymMapping, ожидая получить два объекта, которые я сохранил.
Когда я изменяю сопоставление значений, которые будут нетерпеливыми (как показано в коде по пропущенной строке), и повторите тест, я получаю четыре совпадения.
Я очистил базу данных между запусками, и я могу дублировать эту проблему, заменяя между нетерпением и ленивым.
Я думаю, что это связано с объединениями, создаваемыми под гибернацией, но я не могу найти определенный ответ в Интернете.
Может ли кто-нибудь сказать мне, почему горячая выборка дублирует объекты?
Спасибо.
Ответы
Ответ 1
Как правило, не рекомендуется использовать целевую выборку в сопоставлении - лучше указать желаемые соединения в соответствующих запросах (если вы не уверены на 100%, что при любых обстоятельствах ваш объект не будет иметь смысл/быть действительным без заполнения этой коллекции).
Причина, по которой вы получаете дубликаты, заключается в том, что Hibernate внутренне объединяет ваши корневые и коллекционные таблицы. Обратите внимание, что они действительно дубликаты, например. для 2 SynonymMappings с 3 элементами коллекции каждый из них получит 6 результатов (2x3), 3 копии каждого объекта SynonymMapping. Таким образом, самым простым способом является завершение результатов в наборе, гарантируя, что они уникальны.
Ответ 2
Я вступил в ту же проблему - когда вы устанавливаете FetchType.EAGER для @CollectionOfElements, Hibernate пытается получить все за один снимок, т.е. используя один единственный запрос для каждой записи элемента, связанного с "основным" объектом. Эта проблема может быть успешно решена за счет запроса N + 1, если вы добавите аннотацию @Fetch (FetchMode.SELECT) в свою коллекцию.
В моем случае я хотел иметь объект MediaObject с коллекцией элементов метаданных (видеокодек, аудиокодек, размеры и т.д.). Отображение коллекции metadataItems выглядит следующим образом:
@CollectionOfElements (targetElement = String.class, fetch = FetchType.EAGER)
@JoinTable(name = "mo_metadata_item", joinColumns = @JoinColumn(name = "media_object_id"))
@MapKey(columns = @Column(name = "name"))
@Column (name = "value")
@Fetch (FetchMode.SELECT)
private Map<String, String> metadataItems = new HashMap<String, String>();
Ответ 3
Я столкнулся с этой проблемой, и решил ее с помощью
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
Это очищает дубликаты, вызванные присоединением к дочерним таблицам.
Ответ 4
Вы можете использовать предложение SELECT DISTINCT (Hibernate Query Language) следующим образом
SELECT DISTINCT synonym FROM SynonymMapping synonym LEFT JOIN FETCH synonym.values
Предложение DISTINCT удаляет повторяющиеся ссылки в Hibernate.
Хотя и сборка компонентов, и набор значений имеют свой жизненный цикл, связанный с классом принадлежащих сущностям, вы должны объявить их в предложении select, чтобы их получить. (LEFT JOIN FETCH синоним.значения)
Ответ ChssPly76 - это еще один подход, но не забывает переопределить equals и метод hashcode в соответствии с Set semantic
С уважением,
Ответ 5
Вместо FetchMode.SELECT
с N + 1 запросами лучше использовать BatchSize
e.q. @BatchSize(size = 200)
.
DISTINCT
и Criteria.DISTINCT_ROOT_ENTITY
не помогают, если вам нужно получить более 1 ассоциации. Для этого случая см. Другие решения: fooobar.com/info/61895/...