Consider the following legacy data model which I want to represent in JPA:
- Languages have
id
andname
. - Language Descriptions have a composite primary key consisting of an
id
and alanguage_id
and, additionally, adescription
such that for each translation there is one entry for every language. - Articles have (internationalized) descriptions, identified by the aforementioned
languagedescription_id
.
Now, my first approach was to model the entities like
@Entity
public class Language {
@Id
@Column(length = 3)
private String id;
private String languageDescription;
// ...
}
@Embeddable
public class LanguageDescriptionId implements Serializable {
@Column(length = 9)
private String id;
@Column(length = 3)
private String languageId;
// ...
}
@Entity
public class LanguageDescription {
@EmbeddedId
private LanguageDescriptionId languageDescriptionId;
@ManyToOne
@MapsId(value = "languageId")
private Language language;
@Column(length = 60)
private String description;
// ...
}
@Entity
public class Article {
@Id
private String id;
@ManyToOne
private LanguageDescription languageDescription;
}
which a) is not correct, because technically articles can have a list of translations (ideally being a mapping of language to language descriptions to easily fetch a translation for a given language) and b) leads to the database situation, that my ORM (EclipseLink) added both primary key columns of languagedescription
to the article
table, although articles are only referencing the languagedescription_id
to prevent multiple redundant entries. When replacing the Article
entity with
@Entity
public class Article {
@Id
private String id;
@ManyToMany
private List<LanguageDescription> languageDescriptions;
}
my ORM creates a mapping table which I would have to populate by hand and which would extend the (legacy) data model with redundant information. At the moment my approach is to model the Article
entity like
@Entity
public class Article {
@Id
private String id;
@Column(length = 9)
private String languageDescriptionId;
}
while omitting the JPA support and fetching the translations in a @EntityListener
.
Now, is this situation somehow manageable automatically in JPA or are these kind of "implicit" many to many relations without a mapping table not supported in JPA at all?
Example
public class Main {
public static void main(String[] args) {
final Language english = createLanguage("language_1", "English");
final Language german = createLanguage("language_2", "German");
createArticle1(english, german);
createArticle2(english, german);
}
private static Language createLanguage(String id, String languageDescription) {
final Language language = new Language();
language.setId(id);
language.setLanguageDescription(languageDescription);
return language;
}
private static void createArticle1(Language english, Language german) {
final String languageDescriptionId = "languagedescription_1";
final LanguageDescription languageDescription1 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId1 = new LanguageDescriptionId();
languageDescriptionId1.setId(languageDescriptionId);
languageDescriptionId1.setLanguageId(english.getId());
languageDescription1.setLanguageDescriptionId(languageDescriptionId1);
languageDescription1.setLanguage(english);
languageDescription1.setDescription("Engine");
final LanguageDescription languageDescription2 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId2 = new LanguageDescriptionId();
languageDescriptionId2.setId(languageDescriptionId);
languageDescriptionId2.setLanguageId(german.getId());
languageDescription2.setLanguageDescriptionId(languageDescriptionId2);
languageDescription2.setLanguage(german);
languageDescription2.setDescription("Motor");
final Article article1 = new Article();
article1.setId("a_1");
article1.setLanguageDescriptionId(languageDescriptionId);
}
private static void createArticle2(Language english, Language german) {
final String languageDescriptionId = "languagedescription_2";
final LanguageDescription languageDescription3 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId3 = new LanguageDescriptionId();
languageDescriptionId3.setId(languageDescriptionId);
languageDescriptionId3.setLanguageId(english.getId());
languageDescription3.setLanguageDescriptionId(languageDescriptionId3);
languageDescription3.setLanguage(english);
languageDescription3.setDescription("Turn Signal");
final LanguageDescription languageDescription4 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId4 = new LanguageDescriptionId();
languageDescriptionId4.setId(languageDescriptionId);
languageDescriptionId4.setLanguageId(german.getId());
languageDescription4.setLanguageDescriptionId(languageDescriptionId4);
languageDescription4.setLanguage(german);
languageDescription4.setDescription("Blinker");
final Article article2 = new Article();
article2.setId("a_2");
article2.setLanguageDescriptionId(languageDescriptionId);
}
}