Sto scrivendo un'applicazione in cui tutte le proprietà String devono essere localizzate, vale a dire che devono memorizzare un valore diverso per ogni Locale disponibile. Una soluzione rapida sarebbe quella di utilizzare una mappa, che può essere facilmente mappato in Hibernate, ma non è bello sul programmatore Java:Mappare una stringa localizzata in Hibernate: qualsiasi best practice?
public class Product {
private Map<Locale, String> description;
private Map<Locale, String> note;
pertanto implementato un oggetto LocalString che può contenere stringhe diverse per i diversi locali:
public class LocalString {
private Map<Locale, String> localStrings;
L'oggetto di dominio diventa
public class Product {
private LocalString description;
private LocalString note;
Come faccio migliore cartina questi oggetti con annotazioni Hibernate?
Penso che il miglior mappatura sarebbe stato fatto utilizzando LocalString come componente:
@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;
}
Tutto va bene fino ad ora: il compito formica hbm2ddl crea due tabelle, una Tabella "Prodotti" e una tabella "Prodotti_localStrings" che contiene colonne chiave e valore. Tutto si rompe quando aggiungo il getter per la seconda proprietà:
@Embedded
public LocalString getNote() {
return note;
}
La seconda proprietà non compare nello schema. Ho provato ad utilizzare il tag @AttributesOverride per definire nomi diversi per le due colonne, ma lo schema generato non è corretto:
@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;
}
Nello schema generato, la colonna chiave è scomparso e gli usi della chiave primaria "descrizione" , che non è corretto:
create table Product_localStrings (Product_id bigint not null, note varchar(255), description varchar(255), primary key (Product_id, description));
Un modo per risolvere questo problema?
Sarebbe meglio senza componenti incorporati, utilizzando LocalString come entità?
Eventuali disegni alternativi?
Grazie.
EDIT
ho provato con una mappatura XML e sono riuscito ad ottenere uno schema adeguato, ma inserti riuscire con una violazione di chiave primaria, perché Hibernate genera due inserti invece di uno solo
<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>
Lo schema è
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);
Log:
01.235.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'
Come si comunica che la sospensione genera solo un singolo inserto come il seguente?
insert into libro_strings (ID, idx, descrizione, giudizio) values (?, ?, ?, ?)
EDIT 2011.Apr.05
Sono stato utilizzando la soluzione Map per un po '(annotato con @ElementCollection), fino a quando mi sono imbattuto in due problemi:
- L'API Criteria corrente non funziona sulle raccolte incorporate: http://opensource.atlassian.com/projects/hibernate/browse/HHH-3646
- L'API di Hibernate Search (Lucene) corrente non funziona sulle raccolte incorporate: http://opensource.atlassian.com/projects/hibernate/browse/HSEARCH-566
So che ci sono molte soluzioni alternative, come l'utilizzo di HQL invece di criteri e definire il proprio FieldBridge per prendersi cura di mappa in Lucene, ma non mi piace soluzioni alternative: lavorano fino al prossimo problema viene intorno . Così sto seguendo questo approccio:
mi definiscono un "LocalString" classe che detiene locale e il valore (il locale è in realtà il codice ISO3):
@MappedSuperclass
public class LocalString {
private long id;
private String localeCode;
private String value;
Poi mi definisco, per ogni proprietà voglio localizzare, una sottoclasse di LocalString, che è vuoto:
@Entity
public class ProductName extends LocalString {
// Just a placeholder to name the table
}
ora posso usare nel mio oggetto del prodotto:
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;
}
Con questo approccio devo scrivere una classe vuota per ogni proprietà che ho bisogno di localizzare, che è necessaria per creare una tabella univoca per quella proprietà. Il vantaggio è che posso utilizzare criteri e ricerca senza restrizioni.
Altri esperimenti rivelano che AttributeOverrides collassa sia la chiave che la colonna del valore della mappa in una singola colonna. – xtian
Ho provato con una mappatura xml e sono riuscito a ottenere uno schema valido, ma lo sql generato da Hibernate memorizza ciascuna proprietà su un inserto diverso, terminando con una violazione del vincolo di chiave primaria. – xtian
Onestamente, questo è molto più di quanto ho tempo di leggere e presumo che ci siano più domande incluse. Ottieni un supporto molto migliore quando puoi ridurre il problema a una singola domanda che è facile da descrivere. –