开发者

Hibernate map collection with constant key

I'm trying to map a collection (of type map) using a foreign key and a fixed value as the key/mapping arguments.

I have several tables of product types and a language table which holds stuff like product names and so on.

Now let's say we have an Accessory table which holds (obviously) accessories, then the name of an accesory is stored in the language table with language.id = accessory.id and language.type='accessory'. The map-key should be the language.lang field, a language-code string.

Now, no matter what I've tried, I just can't get the "language.type='accessory'" part right, doesn't like multiple key element, which in turn wouldn't allow elements anyways.

I've also tried it with a composite component as foreignKey, with the constant set by default, but that didn't really work either:

<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"></propert开发者_如何学Cy>
        <property name="Value"></property>
    </class>

of course with the appropriate classes. This approach gives no error, but doesn't fill the HashMap of the Accessory class either...

any help would be appreciated, thanks.

[edit] I now tried it with property-ref as ziodberg suggested, first with a not with like this:

<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>

and

<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>

but this doesn't work, either, it gives

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

which doesn't turn up any helpful information on google.

any ideas?


Edited. So I'm still not quite clear on your table structure - that's what has thrown me when I wrote the initial answer.

My understanding is that your Language table look something like:

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

Does that look about right? If so, this raises the question on how you intend to localize multiple fields within the same entity. Shouldn't you at least have another column linking to property (field) name?

At any rate, <formula> is indeed not supported as part of the <key> (I could have sworn that is is) so your options here are pretty limited:

  1. Use a custom loader - this is probably the most straightforward approach.
  2. Add type as an actual column on your Accessory table and map it as part of composite id; you can then map Language map using composite key. Should work but is rather ugly.
  3. Rethink your approach. Do you really need to load a map of languages every time? Seems like doing it as a separate query (or even direct get() assuming appropriate composite id) for current language would be easier and (with adequate caching configuration) a lot faster.

Update To elaborate on #3 above:

First of, let me clarify this - I'm assuming your localized strings are user-editable at runtime. If that's not the case, I'd strongly recommend forgoing the table altogether and going with resource bundles instead.

I'd map Language as a separate entity with type, id, language and (if you need to do this for multiple properties) property_name as parts of composite id. The actual localizable property on your entity (e.g. ProductName on AccessoryType) would be transient. You can then manually load the appropriate localized string for your entity by doing something like:

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

You can even do that in the event listener assuming that currently selected language is globally obtainable (via global or thread local call) - it will then happen auto-magically for all you entities. If the language table is reasonably small, you can have Hibernate cache it and you won't even incur database hits on this.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜