Коллекция карт Hibernate с постоянным ключом

Я пытаюсь сопоставить коллекцию (карты типов), используя внешний ключ и фиксированное значение в качестве аргументов ключа/сопоставления.

У меня есть несколько таблиц типов продуктов и языковая таблица, в которой хранятся такие вещи, как названия продуктов и так далее.

Теперь предположим, что у нас есть таблица аксессуаров, которая содержит (очевидно) аксессуары, тогда имя аксессуара сохраняется в языковой таблице с помощью language.id = accessory.id и language.type='accessory'. Ключ карты должен быть полем language.lang, строкой кода языка.

Теперь, независимо от того, что я пробовал, я просто не могу правильно понять часть «language.type='accessory'», мне не нравится множественный ключевой элемент, который, в свою очередь, все равно не разрешает элементы.

Я также пробовал это с составным компонентом как foreignKey, с константой, установленной по умолчанию, но это тоже не сработало:

<class name="AccessoryTypes"
    table="accessorytypes">
    <id name="id" column="id" type="java.lang.Long" unsaved-value="0">
        <generator class="identity"></generator>
    </id>
    <map name="Name" table="ProductCode">
        <key column="CompositeId" />
        <map-key column="Language" type="string" />
        <one-to-many class="ProductCode" />
    </map>
</class>

<class name="Language"
        table="Language">
        <composite-id name="compositeId" class="languageKey">
            <key-property name="Type"></key-property>
            <key-property name="Id"></key-property>
        </composite-id>
        <property name="Lang" type="string"></property>
        <property name="Value"></property>
    </class>

конечно с соответствующими классами. Этот подход не дает ошибок, но также не заполняет HashMap класса Accessory...

любая помощь будет оценена, спасибо.

[править] Теперь я попробовал это со свойством-ссылкой, как предложил Зиодберг, сначала с не таким:

<class name="AccessoryTypes"
        table="accessorytypes">
    <id name="id" column="id" type="java.lang.Long" unsaved-value="0">
        <generator class="identity"></generator>
    </id>
    <properties name="CompositeId" >
        <property name="id" />
        <property name="Type" formula="'accessory'" />
    </properties>
    <map name="Name" table="ProductCode">
        <key property-ref="CompositeId" />
        <map-key column="Language" type="string" />
        <one-to-many class="ProductCode" />
    </map>
</class>

и

<class name="com.swissclick.wesco.web.model.ProductCode"
        table="ProductCode">
    <composite-id  class="com.swissclick.wesco.web.model.ProductCodeKey" mapped="true">
        <key-property name="Type"></key-property>
        <key-property name="id"></key-property>
    </composite-id>
    <property name="Language" type="string"></property>
    <property name="Value"></property>
</class>

но это тоже не работает, это дает

org.hibernate.MappingException: collection foreign key mapping has wrong number of columns: AccessoryTypes.Name type: component[Id,Type]

который не дает никакой полезной информации в Google.

Любые идеи?


person Zenon    schedule 17.11.2009    source источник
comment
Может быть, потому что в вашей коллекции вы ссылаетесь на column=compositeId. Как насчет использования вместо этого свойства-ref=compositeId?   -  person Zoidberg    schedule 17.11.2009


Ответы (1)


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

Насколько я понимаю, ваша таблица Language выглядит примерно так:

Id -- this is a PK of entity (e.g. Accessory) this entry provides localization info for
Type -- string constant describing entity type (e.g. "accessory")
Language -- language code
Value -- actual localized string

Это выглядит правильно? Если это так, возникает вопрос о том, как вы собираетесь локализовать несколько полей в одном объекте. Разве у вас не должно быть хотя бы еще одного столбца, связанного с именем свойства (поля)?

В любом случае, <formula> действительно не поддерживается как часть <key> (я мог бы поклясться, что это так), поэтому ваши возможности здесь довольно ограничены:

  1. Используйте пользовательский загрузчик. - это, пожалуй, самый простой способ.
  2. Добавьте type в качестве фактического столбца в таблицу Accessory и сопоставьте его как часть составного идентификатора; затем вы можете сопоставить карту Language с помощью составного ключа. Должно работать, но довольно уродливо.
  3. Переосмыслите свой подход. Вам действительно нужно каждый раз загружать карту языков? Похоже, сделать это в виде отдельного запроса (или даже прямого get() при наличии соответствующего составного идентификатора) для текущего языка было бы проще и (при соответствующей конфигурации кэширования) намного быстрее.

Обновление. Чтобы уточнить пункт 3 выше:

Во-первых, позвольте мне пояснить: я предполагаю, что ваши локализованные строки редактируются пользователем во время выполнения. Если это не так, я настоятельно рекомендую вообще отказаться от таблицы и вместо этого использовать пакеты ресурсов.

Я бы сопоставил Language как отдельный объект с type, id, language и (если вам нужно сделать это для нескольких свойств) property_name как часть составного идентификатора. Фактическое локализуемое свойство вашего объекта (например, ProductName на AccessoryType) будет временным. Затем вы можете вручную загрузить соответствующую локализованную строку для вашего объекта, выполнив что-то вроде:

AccessoryType accessory = ...;
Language language = (Language) session.get(Language.class,
 new LanguageKey("accessory", accessory.id, "en_US", "productName"));
accessory.setProductName(language.getValue());

Это можно сделать даже в событии listener, предполагая, что текущий выбранный язык доступен глобально (через глобальный или локальный вызов потока) - тогда это произойдет автоматически для всех ваших объектов. Если языковая таблица достаточно мала, вы можете использовать Hibernate кэшируйте его, и вы даже не подвергнетесь обращениям к базе данных.

person ChssPly76    schedule 18.11.2009
comment
на самом деле это было первое, что я попробовал, но, похоже, это не работает в Hibernate 3, ключевой элемент не принимает элемент формулы. Таким образом, это приводит к тому, что ключ типа содержимого элемента должен соответствовать (столбец) *. ошибка. - person Zenon; 18.11.2009
comment
да, вот так выглядит языковая таблица. Что ж, производительность на самом деле не проблема (и никогда не будет, насколько я знаю), поэтому я подумал, что было бы довольно элегантно, если бы все объекты базы данных просто несли с собой свои разные интернационализированные строки (к тому же, есть только 3 разных языка ). Думаю, я, вероятно, выберу вариант 1, но не могли бы вы уточнить вариант 3? Что именно вы подразумеваете под прямым get() ? О, и заранее спасибо за ваши усилия, эта проблема сводила меня с ума последние несколько дней... - person Zenon; 18.11.2009
comment
Большое спасибо за вашу помощь, я не думаю, что смог бы понять это в ближайшее время сам. В конце концов, это мой первый проект Java/Hibernate/Spring. Я бы сразу пошел с пакетами ресурсов, если бы клиент явно не запросил базу данных. Метод get() с прослушивателем событий звучит великолепно, теперь немного больше работы, но намного меньше в долгосрочной перспективе. Мне было интересно, есть ли способ заставить прослушиватели событий прослушивать только события, относящиеся к определенным классам (+ производным)? Я обновлю свой вопрос своим решением, как только закончу, если кто-нибудь еще столкнется с той же проблемой. - person Zenon; 19.11.2009
comment
Всегда пожалуйста. Для ядра прослушиватель Hibernate регистрируется только глобально (то есть для каждого сеанса или фабрики сеансов, но для ВСЕХ сущностей). Тем не менее, простой трюк здесь состоит в том, чтобы создать интерфейс с методом обратного вызова, реализовать его в заинтересованных классах сущностей, а затем проверить экземпляр instanceof в вашем слушателе и вызвать метод. Вы также можете сделать это с помощью только отражения (через предопределенную сигнатуру метода) без дополнительного интерфейса. Если бы вместо этого вы использовали Hibernate EntityManager, это было бы сделано за вас — вы просто аннотировали бы метод объекта как @PostLoad, и он был бы вызван. - person ChssPly76; 19.11.2009