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和它自己的映射定義。