【问题标题】:Hibernate map collection with constant key具有常量键的休眠地图集合
【发布时间】:2009-11-17 15:45:05
【问题描述】:

我正在尝试使用外键和固定值作为键/映射参数来映射(映射类型的)集合。

我有几个产品类型表和一个包含产品名称等内容的语言表。

现在假设我们有一个附件表,其中包含(显然)附件,然后附件的名称存储在语言表中,其中 language.id = accessory.id 和 language.type='accessory'。 map-key 应该是 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>

当然有适当的类。这种方法不会报错,但也不会填充 Accessory 类的 HashMap...

任何帮助将不胜感激,谢谢。

[编辑] 我现在按照 ziodberg 的建议使用 property-ref 进行了尝试,首先使用 not with 这样的:

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

这不会在谷歌上找到任何有用的信息。

有什么想法吗?

【问题讨论】:

  • 会不会是因为在您的收藏中您引用了 column="compositeId" 使用 property-ref="compositeId" 怎么样?

标签: hibernate map collections key


【解决方案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

这看起来对吗?如果是这样,这就提出了您打算如何在同一实体中本地化 多个 字段的问题。你不应该至少有另一列链接到属性(字段)名称吗?

无论如何,&lt;formula&gt; 确实不被支持作为&lt;key&gt; 的一部分(我可以发誓就是这样),所以你在这里的选择非常有限:

  1. 使用custom loader - 这可能是最直接的方法。
  2. type 添加为Accessory 表上的实际列,并将其映射为复合ID 的一部分;然后,您可以使用复合键映射 Language 映射。应该可以工作,但相当丑陋。
  3. 重新考虑您的方法。您真的每次都需要加载语言地图吗?似乎将其作为 当前 语言的单独查询(甚至直接 get() 假设适当的复合 id)会更容易并且(具有足够的缓存配置)更快。

更新详细说明上面的#3:

首先,让我澄清一下 - 我假设您的本地化字符串在运行时是用户可编辑的。如果不是这种情况,我强烈建议您完全放弃该表格并使用资源包。

我会将Language 映射为一个单独的实体,其中typeidlanguage 和(如果您需要对多个属性执行此操作)property_name 作为复合ID 的一部分。您实体上的实际可本地化属性(例如,AccessoryType 上的 ProductName)将是暂时的。然后,您可以通过执行以下操作为您的实体手动加载适当的本地化字符串:

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

您甚至可以在event listener 中执行此操作,假设当前选择的语言是全局可获取的(通过全局或线程本地调用) - 然后它将为您的所有实体自动神奇地发生。 如果语言表相当小,您可以使用 Hibernate cache it,而且您甚至不会因此而导致数据库命中。

【讨论】:

  • 这实际上是我尝试的第一件事,但在 Hibernate 3 中似乎不起作用,关键元素不采用公式元素。所以这会导致“元素类型“key”的内容必须匹配“(column)*”。错误。
  • 是的,这就是语言表的样子。好吧,性能并不是真正的问题(而且永远不会是,我知道这么多),所以我认为如果所有数据库对象都只携带它们不同的国际化字符串(而且,只有 3 种不同的语言)。我想我可能会选择选项 1.,但您能否详细说明 3.? “直接get()”到底是什么意思?哦,提前感谢您的努力,在过去的几天里,这个问题一直让我发疯......
  • 非常感谢您的帮助,我不认为我自己能很快解决这个问题。毕竟,这是我的第一个 Java/Hibernate/Spring 项目。如果客户没有明确要求数据库,我会立即使用资源包。带有事件侦听器的 get() 方法听起来很棒,现在工作量增加了一点,但从长远来看,工作量要少得多。我想知道,有没有办法让事件监听器只监听与某些类(+派生)有关的事件?完成后,如果其他人遇到同样的问题,我会用我的解决方案更新我的问题。
  • 不客气。对于核心 Hibernate 监听器仅全局注册(即,每个会话或会话工厂,但对于 ALL 实体)。然而,这里的简单技巧是创建一个带有回调方法的接口,让感兴趣的实体类实现它,然后在您的侦听器中检查“instanceof”并调用该方法。您也可以仅使用反射(通过预定义的方法签名)来执行此操作,而无需额外的接口。如果您要改用 Hibernate EntityManager,这将为您完成 - 您只需将实体上的方法注释为 @PostLoad 并调用它。
猜你喜欢
  • 2023-03-14
  • 2014-01-27
  • 2014-07-22
  • 1970-01-01
  • 2011-01-07
  • 2011-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多