8

我正在编写一个应用程序,其中所有字符串属性都必须进行本地化,即它们必须为每个可用的语言环境存储不同的值。 一个快速的解决办法是使用地图,它可以在Hibernate中很容易映射,但不是很好的Java程序员:在Hibernate中映射一个本地化的字符串 - 任何最佳实践?

public class Product { 
    private Map<Locale, String> description; 
    private Map<Locale, String> note; 

因此,我实现了一个LocalString对象,可以持有不同的地区不同的字符串:

public class LocalString { 
    private Map<Locale, String> localStrings; 

域对象变成

public class Product { 
    private LocalString description; 
    private LocalString note; 

我最好如何映射与Hibernate的注解这些对象呢?

我认为最好的映射将使用LocalString作为一个组件来完成:

@Embeddable 
public class LocalString { 
    private Map<Locale, String> localStrings; 

    @ElementCollection 
    public Map<Locale, String> getLocalStrings() { 
     return localStrings; 
    } 

...

@Entity 
public class Product { 
    private Long id; 
    private LocalString description; 
    private LocalString note; 

    @Embedded 
    public LocalString getDescription() { 
      return description; 
    } 

全部是迄今为止罚款:在蚂蚁就是hbm2ddl任务创建了两个表,一个“产品”表和一个包含键和值列的“Products_localStrings”表。

@Embedded 
    public LocalString getNote() { 
      return note; 
    } 

第二属性不会在模式显示:当我添加了吸气的第二个属性 一切打破。 我尝试使用@AttributesOverride标签为两列定义不同的名称,但生成的模式是不正确的:

@Embedded 
    @AttributeOverrides({ 
      @AttributeOverride(name="localStrings", [email protected](name="description")) 
    }) 
    public LocalString getDescription() { 
      return description; 
    } 
    @Embedded 
    @AttributeOverrides({ 
      @AttributeOverride(name="localStrings", [email protected](name="note")) 
    }) 
    public LocalString getNote() { 
      return note; 
    } 

在生成的模式,关键柱已经消失,主键用途“说明” ,这是不正确的:

create table Product_localStrings (Product_id bigint not null, note varchar(255), description varchar(255), primary key (Product_id, description)); 

任何方法来解决这个问题?

如果没有嵌入式组件,使用LocalString作为实体会更好吗?

任何替代设计?

谢谢。

编辑

我试着用XML映射,我得到了一个合适的模式,但因为Hibernate生成两个刀片,而不是只是一个

<hibernate-mapping> 
    <class name="com.yr.babka37.demo.entity.Libro" table="LIBRO"> 
     <id name="id" type="java.lang.Long"> 
      <column name="ID" /> 
      <generator class="org.hibernate.id.enhanced.SequenceStyleGenerator"/> 
     </id> 
     <property name="titolo" type="java.lang.String"> 
      <column name="TITOLO" /> 
     </property> 
     <component name="descrizioni" class="com.yr.babka37.entity.LocalString"> 
     <map name="localStrings" table="libro_strings" lazy="true" access="field"> 
      <key> 
       <column name="ID" /> 
      </key> 
      <map-key type="java.lang.String"></map-key> 
      <element type="java.lang.String"> 
       <column name="descrizione" /> 
      </element> 
     </map> 
     </component> 
     <component name="giudizi" class="com.yr.babka37.entity.LocalString"> 
     <map name="localStrings" table="libro_strings" lazy="true" access="field"> 
      <key> 
       <column name="ID" /> 
      </key> 
      <map-key type="java.lang.String"></map-key> 
      <element type="java.lang.String"> 
       <column name="giudizio" /> 
      </element> 
     </map> 
     </component> 
    </class> 
</hibernate-mapping> 

插入失败,一个主键冲突该模式是

create table LIBRO (ID bigint not null auto_increment, TITOLO varchar(255), primary key (ID)); 
create table libro_strings (ID bigint not null, descrizione varchar(255), idx varchar(255) not null, giudizio varchar(255), primary key (ID, idx)); 
alter table libro_strings add index FKF576CAC5BCDBA0A4 (ID), add constraint FKF576CAC5BCDBA0A4 foreign key (ID) references LIBRO (ID); 

登录:

DEBUG org.hibernate.SQL - insert into libro_strings (ID, idx, descrizione) values (?, ?, ?) 
DEBUG org.hibernate.SQL - insert into libro_strings (ID, idx, giudizio) values (?, ?, ?) 
WARN o.h.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000 
ERROR o.h.util.JDBCExceptionReporter - Duplicate entry '5-ita_ITA' for key 'PRIMARY' 

我该如何告诉hibernate只生成一个像下面这样的插入?

insert into libro_strings (ID, idx, descrizione, giudizio) values (?, ?, ?, ?) 

编辑在2011.Apr.05

我已经使用了一段时间的地图解决方案(与@ElementCollection注释)一直,直到我偶然发现了两个问题:

我知道有很多解决方法,如使用的,而不是标准HQL和定义自己的FieldBridge拿地图在Lucene的照顾,但我不喜欢的解决方法:他们的工作,直到下一个问题恶有恶报。 所以我现在下面这个方法:

我定义一个类“LocalString”保存语言环境和值(区域设置实际上是ISO3代码):

@MappedSuperclass 
public class LocalString { 
private long id; 
private String localeCode; 
private String value; 

然后我定义,每个属性我要本地化,LocalString的一个子类,里面是空的:

@Entity 
public class ProductName extends LocalString { 
    // Just a placeholder to name the table 
} 

现在我可以在我的产品对象使用它:

public class Product { 
    private Map<String, ProductName> names; 

    @OneToMany(cascade=CascadeType.ALL, orphanRemoval=true) 
    @JoinColumn(name="Product_id") 
    @MapKey(name="localeCode") 
    protected Map<String, ProductName> getNames() { 
    return names; 
    } 

通过这种方法,我必须为每个需要本地化的属性编写一个空类,这需要为该属性创建一个唯一的表。 好处是我可以不受限制地使用Criteria和Search。

+0

进一步的实验显示AttributeOverrides将地图的键和值列都折叠为一个列。 – xtian 2011-01-28 15:21:27

+0

我尝试了一个xml映射,我设法得到了一个有效的模式,但由Hibernate生成的sql将每个属性存储在不同的插入中,结束于主键约束冲突。 – xtian 2011-01-28 15:23:12

回答

1

您的xml映射不起作用,因为您将值类型映射到同一个表中。当使用元素或复合元素时,数据被视为值类型,属于它所在的类。它需要自己的表。该ID在集合中唯一是唯一的。

它要么映射作为组分:

<component name="descrizioni"> 
     <map name="localStrings" table="descrizioni_strings" ...> 
     <!-- ... --> 
     </map> 
    </component> 
    <component name="giudizi"> 
     <map name="localStrings" table="giudizi_strings" ... > 
     <!-- ... --> 
     </map> 
    </component> 

或者作为独立的实体:

<many-to-one name="descrizioni" class="LocalString"/> 
    <many-to-one name="giudizi" class="LocalString"/> 

在第二种情况下,LocalString是一个实体。它需要一个id和它自己的映射定义。