Hibernate / JPA Map of Primitives вызывает странное левое внешнее соединение - PullRequest
2 голосов
/ 14 февраля 2011

У меня возникают трудности с достижением соответствующего отображения объекта, содержащего карту примитивов (Map).Результирующий SQL - это не то, что я ожидаю (левое внешнее соединение), и в результате получается, что при получении списка указанных сущностей сущности, которые должны быть уникальными, дублируются в результате.

Вот сущность (сокращенно):

@Entity(name = "ZPrincipal")
@Table(name = "users")
public class ZPrincipal implements Principal, Serializable {
    @Id
    @Column(name = "username")
    private String username;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "user_metadata", joinColumns = { @JoinColumn(name = "username") })
    @MapKeyColumn(name = "meta_key")
    @Column(name = "meta_value", nullable = false)
    private Map<String, String> metadata;
}

Как видите, существует пользовательский класс (ZPrincipal), который содержит карту.Мои таблицы выглядят так (опять же, сокращая некоторые поля от «пользователей», таких как электронная почта, пароль и т. Д.):

CREATE TABLE users (
    username VARCHAR(60) NOT NULL,
    PRIMARY KEY (username)
);

CREATE TABLE user_metadata (
    username VARCHAR(60) NOT NULL,
    meta_key VARCHAR(255) NOT NULL,
    meta_value VARCHAR(1024) NOT NULL,
    CONSTRAINT user_meta_fk FOREIGN KEY (username) REFERENCES users (username),
    PRIMARY KEY (username, meta_key)
);

Некоторые примеры содержимого:

Таблица пользователей:

|username |
+---------+
|admin    |
|brett    |
+---------+

Таблица метаданных:

|username |meta_key  |meta_value  |
+---------+----------+------------+
|brett    |key1      |value1      |
|brett    |key2      |value2      |
+---------+----------+------------+

Используя приведенное выше отображение, когда я использую Hibernate для получения списка ZPrincipals, например так:

Criteria criteria = session.createCriteria(ZPrincipal.class);
List<ZPrincipal> list = criteria.list();

Hibernate запускает следующий запрос:

select this_.username as username9_1_, 
       metadata2_.username as username9_3_, 
       metadata2_.meta_value as meta2_3_, 
       metadata2_.meta_key as meta3_3_
from users this_ 
left outer join user_metadata metadata2_ on this_.username=metadata2_.username

В результате возвращаются строки:

|username9_1_  |username9_3_  |meta2_3_  |meta3_3_  |
+--------------+--------------+----------+----------+
|admin         |null          |null      |null      |
|brett         |brett         |key1      |value1    |
|brett         |brett         |key2      |value2    |
+--------------+--------------+----------+----------+

В результате получается список пользователей, содержащий 3 сущности (то есть "проблема"), "admin "объект пользователя и два" бретта "объекта пользователя (идентичные).Оба «бретт» экземпляра действительно содержат правильно заполненную карту.Для тех, кому интересно, класс ZPrincipal делает переопределение equals (), которое обеспечивает сравнение на основе имени пользователя (такое же, как первичный ключ), и переопределяет hashCode (), также хэширующее имя пользователя.

Наш магазинработает от «схемы сначала», и схема кажется «правильной» в смысле базы данных.Возможно, эту проблему можно решить с помощью промежуточной таблицы сопоставления и сгенерированных идентификаторов в таблице user_metadata, но из чтения доступной документации JPA и Hibernate (http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection) кажется, что сопоставление набора примитивных элементов с использованием только двух таблицдолжно быть возможно.

Чего не хватает в отображении? Или это ошибка в Hibernate? И если да, кто-нибудь может придумать обходной путь сопоставления? Я немного обшарил аннотациинет радости.

1 Ответ

2 голосов
/ 14 февраля 2011

Это ожидаемое и задокументированное поведение.Экземпляры - это один и тот же объект (поскольку hibernate гарантирует, что один и тот же объект с одним и тем же первичным ключом будет иметь ровно один экземпляр в JVM).Вы можете сделать отдельную сторону клиента, чтобы удалить их.Вы можете прочитать часто задаваемые вопросы по Hibernate, чтобы понять, почему они решили не делать это автоматически.

То, что вы пытались с помощью "FROM ZPrincipal", - это сказать Hibernate, чтобы он не выполнял активную выборку коллекции, и поэтому ему придетсязагружать карту для каждой сущности на лету (что может привести к проблеме выбора N + 1, если вы собираетесь читать записи карты каждого извлеченного ZPrincipals. (Либо это, либо Hibernate выдает дополнительные запросы для заполнения карты).).

Лучший способ - выполнить дедупликацию самостоятельно и сохранить несколько запросов.

Ссылка: http://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...