Mandantenfähigkeit mit Hibernate (ohne Hibernate Shards)

19.09.2017
Von: Michael Buchholz

Zur Realisierung von Mandantenfähigkeit in einer Anwendung verwenden wir unterschiedliche DB-Schemata. Dies hat den Vorteil, dass die Daten pro Mandant in eigenen Tabellen liegen und auf Datenbankebene die Berechtigungen gesteuert werden können. In unserem Fall existiert ein Schema für gemeinsam genutzte (Stamm-) Daten. Pro Mandant gibt es ein weiteres Schema für die mandantenspezifischen Daten, wobei die Mandanten-Schemata alle identisch aufgebaut sind.

Die Entities für die Stammdaten werden in JPA mit der @Table-Annotation versehen, wobei das Schema-Attribut jeweils angegeben wird: @Table(name="...", schema="<Stammdatenschema>")
Bei den Entities für die mandantenspezifischen Daten wird das Schema-Attribut in der @Table-Annotation nicht angegeben: @Table(name="..."). Hier greift das Default-Schema, welches Hibernate dann automatisch in den generierten SQL-Statements vor die Tabellennamen setzt. Das Default-Schema kann in der persistence.xml festgelegt werden:

<persistence-unit name="...">
    ...
    <properties>
        <property name="hibernate.default_schema" value="..."/>
        ...
    </properties>
</persistence-unit>

Pro Mandant wird eine eigene Persistence-Unit mit entsprechendem Default-Schema definiert. Im Application Server existiert zu jeder Persistence-Unit ein zugehöriger Entity-Manager. Bei einer Client-Anfrage entscheidet der Server anhand der User-ID, zu welchem Mandant der User gehört und verwendet dann für die Datenbank-Zugriffe den korrespondierenden Entity-Manager.

Alternativ könnte man nachträglich die generierten SQL-Statements manipulieren und das Schema anpassen. Hibernate bietet hierzu die Möglichkeit, einen Interceptor (org.hibernate.Interceptor) in der persistence.xml zu registrieren, der über entsprechende Callback-Methoden vor der Ausführung von DB-Operationen benachrichtigt wird:

<persistence-unit name="...">
    ...
    <properties>
        <property name="hibernate.ejb.interceptor"    
                  value="<MeineInterceptorKlasse>" />
        ...
    </properties>
</persistence-unit>

In der Methode onPrepareStatement(String) wird das von Hibernate generierte SQL-Statement als Parameter übergeben und kann vor der Ausführung noch verändert werden, da das von der Methode zurückgelieferte Statement ausgeführt wird.

Fazit: Die erste Variante ist sicherlich die robustere Lösung, während man mit der Interceptor-Lösung die vollständige Kontrolle über die von Hibernate abgesetzten DB-Statements erlangt.


INDIVIDUELLE SOFTWARE

KONTAKT | IMPRESSUM | DATENSCHUTZ | AGB