Hibernate / JPA: Eine Datenbank und pro User ein Schema

internet

Top Contributor
Hallo,

ich würde meine Applikation gerne auftrennen, sodass ich pro Server eine Datenbank habe, und dann pro User ein eigenes Schema.
Somit könnte ich recht flexibel ein Schema auch auf einen anderen Server migrieren.

Hat hier jemand Erfahrung wie ich das mache?

  • Wie muss die persistence.xml angepasst werden?
  • Wie muss ich meine Queries anpassen, sodass die Abfrage auf das entsprechende Schema geht?
  • Wie läuft der Prozess damit ich weiß auf welches Schema gegangen werden muss?
    • Ich stelle es mir so vor, dass ich in einer Tabelle den Username speichere und zusätzlich das Datenbankschema
    • Das Schema wird beim Registrieren eines Users erstellt. Zudem habe ich eine zentrale Datenbank, die dann Username / Schema speichert.
    • Beim Login habe ich dann in der Session den Username mit Schema. Somit habe ich das Schema im Zugriff?
Danke für jede Hilfe.
 
Separate schemas: Tenants share common database but each tenant has its own set of tables (schema).
Da einfach nur einmal zum Verständnis eine Erläuterung, was das bedeutet:

Du hast in einer Datenbank nicht nur die Struktur:
DB -> Tabelle
sondern
DB -> Schema -> Tabelle

Daher kann der Tabellenname gleich sein und du hast unterschiedliche Schemata.

Das kann dann z.B. so aussehen, dass Du eine Tabelle public.mytable erzeugst: Schema ist public und mytable ist der Tabellenname.

Somit kannst Du z.B. Schemata kundea und kundeb haben und beide haben dann eine Tabelle data. In SQL Anweisungen wäre es dann kundea.data und kundeb.data.

Das Schema wäre somit für jeden user gleich. Wenn Du mit einem User mit Schema kundea zugreifst, dann bekommst Du bei der Tabelle data kundea.data. Wenn Du ein User mit default Schema kundeb zugreifst, dann bekommst Du kundeb.data.

Du musst halt einfach nur nicht das Schema mit angeben bei den Zugriffen. (Oder du hast evtl. noch paar gemeinsame Tabellen - die wären dann in dem public schema.
 
Muss das nicht heißen "Die Tabelle wäre somit für jeden User gleich" ?
Wenn Du nur die einzelne Tabelle betrachtest, dann ja.

Aber es geht ja um das ganze Schema. Für jeden Kunden legst Du immer genau das gleiche Schema an. Wenn das nur eine Tabelle ist, dann ist das nur eine Tabelle. Aber das war ja nur ein Beispiel. Das ganze Schema ist gleich denn so kannst Du immer mit den gleichen Zugriffen arbeiten - du musst nur das Schema setzen.
 
genau, so habe ich es auch verstanden.

Nun ist mir der Prozess aber noch nicht ganz klar, was ich alles brauche / ändern muss innerhalb meiner derzeitigen Applikation.
  1. Das Hibernate Interface muss ich implementieren?
  2. Wo speichere ich die TenantId? Klar 1x in der Datenbank, in der ich eben ein Mapping vom Mandant zur TenantID habe. Aber wird das dann immer in der Session von App - Server gespeichert?
  3. Beim Erstellen eines Mandanten, muss ich ja auch immer ein neues Schema erstellen...
    Wie erstelle ich ein Schema? Bzw. wie gebe ich die ganzen Tabellen mit, die in dem Schema erstellt werden sollen?
  4. Muss ich meine ganzen Queries umbauen, sodass immer die TenantId mitgegeben wird?
Das sind so mal die Fragen.
 
Wie erstelle ich ein Schema? Bzw. wie gebe ich die ganzen Tabellen mit, die in dem Schema erstellt werden sollen?
Dein Server, JPA oder Hibernate bieten dazu Mechanismen an. Du musst das ja jetzt schon irgendwie Updates handhaben. Meistens legt man dazu für jede Version SQL-Scripte an, welche dann der Reihe nach abgearbeitet werden.
Bevor du Fragen zu Multi-Tenancy stellst solltest du erst einmal alles durcharbeiten und evtl. einen Prototyp bauen. Dann wirst du das Meiste verstehen und kannst dann evtl. noch gezielt Fragen stellen. Es weiß im Moment keiner genau wie deine Anwendungen aufgebaut sind, es ist also wie eine Ferndiagnose, das gilt auch für die Queries.
 
Aktuell geschieht solch ein Update anhand Hibernate in der persistence.xml mit:
Java:
<property name="hibernate.hbm2ddl.auto" value="update" />
Neue Felder werden dann entsprechend dem Schema hinzugefügt.

Also im ersten Schritt würden mir die Antworten auf die Fragen schon Mal helfen:
  1. Das Hibernate Interface muss ich implementieren?
  2. Wo speichere ich die TenantId? Klar 1x in der Datenbank, in der ich eben ein Mapping vom Mandant zur TenantID habe. Aber wird das dann immer in der Session von App - Server gespeichert?
  3. Beim Erstellen eines Mandanten, muss ich ja auch immer ein neues Schema erstellen...
    Wie erstelle ich ein Schema? Bzw. wie gebe ich die ganzen Tabellen mit, die in dem Schema erstellt werden sollen?
  4. Muss ich meine ganzen Queries umbauen, sodass immer die TenantId mitgegeben wird?
Queries erstelle ich i.d.R. mit dem CriteriaQuery:

Code:
public <T> T findSingleByQuery(JpaQueryBuilder jpaQueryBuilder, Class<T> entityClass, EntityManager entityManager) { 

            CriteriaBuilder cb = entityManager.getCriteriaBuilder();

            CriteriaQuery<T> cr = cb.createQuery(entityClass);
            Root<T> root = cr.from(entityClass);
            cr.select(root);

            Query query = (Query) entityManager.createQuery(cr);
            entityObject = (T) query.getSingleResult();
}
 
Aktuell geschieht solch ein Update anhand Hibernate in der persistence.xml mit:
Ja das ist der naive Ansatz, das funktioniert für die Entwicklung aber nicht für ernsthafte kommerzielle Projekte. Was würde passieren, wenn du NOT NULL Spalten einfügen musst. Wie kannst du so Datenändern, z.B. in eine andere Tabelle kopieren oder neue Tabellen befüllen.

Queries erstelle ich i.d.R. mit dem CriteriaQuery:
Das Beispiel funktioniert nicht, es gibt nicht mal die Rückgabe.
Und mal ehrlich: Wer baut denn noch solche Spagetti-Code-Queries. Das ist doch Folter. Man soll sowieso JPQL (HQL) bevorzugen oder man macht das gleich vernünftig: https://medium.com/@nagarjunmallesh...ve-study-with-code-implementation-4462b1abcfd.
Ich kenne das Argument, dass der Compiler schon Fehler finden kann, wenn sich etwas ändert. Das ist aber nicht ganz richtig. Bedingungen kann der Compiler auch nicht prüfen. Es sind trotzdem alle Tests erforderlich, in denen man Fehler auch finden sollte. Die Optimierung für die DB's ist auch mäßig. JPA ist ein Molloch, der nichts wirklich einfacher macht und eine zusätzliche Ebene von Fehlern und Komplexität mitbringt. In größeren Projekten wird JPA schnell zur Bremse. Deshalb setzen wird das auch gar nicht mehr ein, wenn es irgend möglich ist.

Das Hibernate Interface muss ich implementieren?
Du benötigst eine Implementierung von MultiTenantConnectionProvider, welche deine Anforderungen erfüllt.
Wo speichere ich die TenantId? Klar 1x in der Datenbank, in der ich eben ein Mapping vom Mandant zur TenantID habe. Aber wird das dann immer in der Session von App - Server gespeichert?
Das kannst du machen wie du willst.
Beim Erstellen eines Mandanten, muss ich ja auch immer ein neues Schema erstellen...
Wie erstelle ich ein Schema? Bzw. wie gebe ich die ganzen Tabellen mit, die in dem Schema erstellt werden sollen?
Wie ich schon weiter oben geschrieben habe: Meistens mit SQL-Schripten, welche in einer bestimmten Reihenfolge ausgeführt werden, z.B. soriert über den Dateinamen: schript_1.0.0.sql, script_1.1.0.sql, script_2.1.0.sql usw.
Muss ich meine ganzen Queries umbauen, sodass immer die TenantId mitgegeben wird?
Nein, normalerweise nicht. Im DB-Schema sind nur die Daten, welche zu der TenantId gehören. Das ist ja der Sinn dahinter.
 

Zurück
Oben