2009-07-07 3 views
35

У меня есть класс с именем SynonymMapping, который имеет коллекцию значений отображенный как CollectionOfElementsHibernate 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, ожидающих получения двух сохраненных мной объектов.

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

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

Я думаю, что это связано с объединениями, которые создает спящий режим под ним, но я не могу найти однозначный ответ в Интернете.

Может ли кто-нибудь сказать мне, почему нетерпеливая выборка дублирует объекты?

Спасибо.

+0

Каждый, у кого есть исключение «Было найдено несколько строк с заданным идентификатором», должен знать об этом. Это действительно экономит много часов, не зная, что, черт возьми, идет не так. См. @ User176668 ответ !! –

ответ

27

Как правило, нецелесообразно обеспечивать надежную выборку в сопоставлении - лучше указать желаемые соединения в соответствующих запросах (если вы не уверены на 100%, что при любых обстоятельствах ваш объект не будет иметь смысл/быть действителен без заполнения этой коллекции).

Причина, по которой вы получаете дубликаты, заключается в том, что Hibernate внутренне объединяет ваши корневые и коллекционные таблицы. Обратите внимание, что они действительно дубликаты, например. для 2 SynonymMappings с 3 элементами коллекции каждый из них получит 6 результатов (2x3), 3 копии каждого объекта SynonymMapping. Таким образом, самым простым решением является обертка результатов в наборе, гарантируя, что они уникальны.

+34

Но почему Hibernate не отфильтровывает их, я не понимаю, почему вы когда-нибудь захотите, чтобы это было так. –

+0

Я могу подтвердить, что он работает. Я вообще использовал коллекцию , и это было ошибкой. –

2

Вы можете использовать SELECT DISTINCT (Hibernate Query Language) пункт следующего

SELECT DISTINCT synonym FROM SynonymMapping synonym LEFT JOIN FETCH synonym.values 

DISTINCT положение удаляет повторяющиеся ссылки в спящем режиме.

Несмотря на то, что коллекция компонентов и значений имеет свой жизненный цикл, связанный с классом принадлежащих сущности, вы должны объявить их в предложении select, чтобы их получить. (LEFT JOIN FETCH synonym.values)

ответ ChssPly76 является другой подход, но не забывает переопределения равных и метод хэш-код в соответствии с установленными семантические

С уважением,

+3

Мне было интересно узнать, почему это происходит и почему было принято решение о спящем режиме, чтобы ответить таким образом больше, чем разные способы обойти его. – Rachel

62

Я вошел в ту же самую проблему - когда вы установите 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>(); 
+2

Большое спасибо за это. – AHungerArtist

+1

Мне понравилось решение, потому что нам не нужно обертывать результат с помощью Set, чтобы получить уникальность. – sanbhat

+0

Спасибо, СООООО за это !!! Сохраненный мой день :) – pasql

5

Я столкнулся с этой проблемой, и я решил его с помощью

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Это очищает дублирующие, которые вызваны Соединить сделаны в дочерние таблицы.

0

Вместо FetchMode.SELECT с N + 1 запросами лучше использовать BatchSize e.q. @BatchSize(size = 200).

DISTINCT и Criteria.DISTINCT_ROOT_ENTITY не поможет, если вам нужно получить более 1 ассоциации. Для этого случая см. Другие решения: https://stackoverflow.com/a/46013654/548473

Смежные вопросы