Подтвердить что ты не робот

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, ожидая получить два объекта, которые я сохранил.

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

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

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

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

Спасибо.

4b9b3361

Ответ 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/...