Hi,
This problem can be solved using following two extended features of
OpenJPA
1. Using entity relation as primary key
2. Using non-standard joins
Sculpture will simply declare a one-to-one relation to its localized
description.
public class Sculpture {
@OneToOne(mappedBy="sculpture")
private LocalizedDescription desc;
Though there are more than one description for a Sculpture, our mapping will
ensure that the relation is 'logically' one-to-one based any given locale.
While the LocalizedDescription class will look like
@Entity
@Table(name="Sculpture_i18n")
@IdClass(LocalizedDescription.CompoundId.class)
public class LocalizedDescription {
@Id
@ManyToOne
@JoinColumns({
@JoinColumn(name="sculpture_id",
referencedColumnName="sculpture_ID"),
@JoinColumn(name="locale", referencedColumnName="'en'")
})
private Sculpture sculpture;
@Id
@Column(name="locale")
private String locale;
...
}
Few noteworthy points in this class are
1. The class uses its entity relation field 'sculpture' as a part of a
compound primary key. This is not supported by JPA specification, but is
supported by OpenJPA.
2. The class uses a separate identity class. A separate key class must be
used when compound key includes an entity relationship field. We will see
shortly what discipline must be followed in definining this separate
identity class.
3. The mapping of
private Sculpture sculpture;
uses non-standard joins using a pair of join columns.
@JoinColumns({
@JoinColumn(name="sculpture_id",
referencedColumnName="sculpture_ID"),
@JoinColumn(name="locale", referencedColumnName="'en'")
})
The first join column is the 'standard' one -- it refers to the
primary key column of Sculpture's table.
The second join column is what makes it non-standard. This join column makes
the join condition from Sculpture to this entity its locale column.
4. The non-standard join column uses a constant value. The default
constant value is set to 'en'. The single quote around 'en' is significant
because this quote distinguishes it as a constant value of the column rather
than a column name.
5. The locale field is also declared as part of the primary key, this
will enable the mapping to put a compound primary key of the form in
'sculpture_i18n' table
PRIMARY KEY (`locale`,`sculpture_id`)
6. The compound key class
public static class CompoundId implements Serializable {
public int sculpture;
public String locale;
comprised of the two fields, their name must match the corresponding
@Id annotated fields. What about the type?
Here comes the special bit about using Entity relationship as primary
key field. The type in the key class is same as the type of the primary key
of the relation. So as Sculpture declares its primary key as
public class Sculpture {
@Id
@Column(name="sculpture_ID")
private int id;
The key class declares its 'sculpture' field's type as int.
Under these mapping OpenJPA will generate the target schema you have
specified in your original mail.
Now the runtime behavior:
The behavior we want is we will issue query or find() as usual, but the
result should fetch the description according to the current locale. But in
our mapping, we have set our constant join value to 'en' -- which will only
bring us English language description. How to change that constant join
value dynamically?
That requires some internal information on OpenJPA's architecture which
makes every metadata available to runtime application. For example, to get
to the foreign key column that is using constant join value and changing it
that is what we need to do:
// naviagate through metadata repository to the foreign key
ForeignKey fk =((FieldMapping)
((OpenJPAEntityManagerFactorySPI)OpenJPAPersistence.cast(emf))
.getConfiguration()
.getMetaDataRepositoryInstance()
.getCachedMetaData(Sculpture.class)
.getField("desc")
.getMappedByMetaData())
.getForeignKey();
// get its constant column and change its value
Locale locale = Locale.FRENCH;
fk.joinConstant(fk.getConstantColumns()[0],
locale.getLanguage());
When the constant value needs to be changed is a application decision -- but
the interesting point is this change will transparently impact any query or
find() that joins to Sculpture_i18n from Sculpture table.
http://n2.nabble.com/file/n527521/NonStandardJoin.zip NonStandardJoin.zip
I have attached a small zip that packs the classes that I have described
here, along with a small JUnit Test case to demonstrate its usage.
It is also possible to refer to the descriptions as Map keyed by their
respective language from Sculpture class. But that solution will not give
you the benefit of transparent locale-specific access described here using
advanced features of OpenJPA.
--
View this message in context:
http://n2.nabble.com/JPA-question-and-i18n-problem-tp471792p527521.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.