Hibernate Map Mapping ;)
Da mich dieses Problem eine ganze Weile beschäftigt hat möchte ich meine Erkenntnisse über die Tücken und Finessen der Abbildung von Java Maps in die Datenbank mittels Hibernate/JPA gerne mit allen Interessierten Lesern teilen.
Es geht darum mittels einer Verbindungstabelle aka JoinTable eine Art ManyToMany Verbindung zwischen zwei Entities herzustellen. Diese soll bi-direktional sein und ein zusätzliches Feld conn_active besitzen, welches angibt ob die aktuelle Kombination momentan aktiv ist oder nicht.
Außerdem sollen Texte übersetzt in verschiedene Sprachen in einer Map gespeichert werden, welche Hibernate in eine eigene Tabelle auslagern soll.
Soweit so gut …
Problem
Der in der Einleitung beschriebene Fall nun nochmal etwas konkreter:
Zur Abbildung der an der FAU verfügbaren Fachrichtungen (aktiv/inaktiv), Abschlüssen (aktiv/inaktiv) und deren Verknüpfungen untereinander (aktiv/inaktiv) wurde das rechts abgebildete Datenbankschema erdacht.
Kurz beschrieben bildet die Tabelle common_conn_subject_graduation_fau die möglichen Kombinationen zwischen den Tabellen common_list_graduation_fau und common_list_subject_fau ab — also die möglichen Fach/Abschluss Kombinationen.
Um das Bild zu vervollständigen, seien auch die beiden Tabellen common_text_subject_fau und common_text_graduation_fau erwähnt, welche jeweils die Namen der Fächer und Abschlüsse übersetzt in verschiedene Sprachen enthalten.
Lösung
Für alle nur schnell wissen wollen wie die Lösung aussieht hier in aller Kürze:
1. Für die common_conn_subject_graduation_fau Verbindungstabelle sieht die entsprechend annotierte Implementierung wie folgt aus.
@Entity
@Table(name = "common_list_subject_fau")
public class SubjectFAU {
...
@CollectionOfElements
@JoinTable(name = "common_conn_subject_graduation_fau")
@org.hibernate.annotations.MapKeyManyToMany(
joinColumns=@JoinColumn(name="common_list_graduation_fau_id")
)
@Column(name = "conn_active")
private Map<GraduationFAU, Boolean> graduations;
...
}
@Entity
@Table(name = "common_list_graduation_fau")
public class GraduationFAU {
...
@CollectionOfElements
@JoinTable(name = "common_conn_subject_graduation_fau")
@org.hibernate.annotations.MapKeyManyToMany(
joinColumns=@JoinColumn(name="common_list_subject_fau_id")
)
@Column(name = "conn_active")
private Map<SubjectFAU, Boolean> graduations;
...
}
2. Ähnlich aber doch nicht gleich können die common_text_graduation_fau bzw. common_text_subject_fau Tabellen umgesetzt werden. Da die Annotationen der Entities beider Tabellen gleich sind, hier exemplarisch nur die Implementierung der SubjectFAU Entity.
@Entity
@Table(name = "common_list_subject_fau")
public class SubjectFAU {
...
@CollectionOfElements
@Enumerated(EnumType.STRING)
@JoinTable(name = "common_text_subject_fau",
uniqueConstraints = { @UniqueConstraint(columnNames = { "locale", "text" }) })
@org.hibernate.annotations.MapKey(columns = { @Column(name = "locale") })
@Column(name = "text")
// @Type(type="text")
private Map<CourseFAULocaleEnum, String> translations;
...
}
Quellen
Für alle die mehr wissen möchten:
Eine sehr gute Schritt-für-Schritt Erklärung zu Collection Mapping und Hibernate gibt es hier. Diese bezieht sich allerdings nur auf primitive Typen als Keys für die Map.
Den entscheidenden Hinweis zur Lösung des Mapping Problems mit einer Entity als Key der Map lieferte dieser Blogpost.