2013-05-01 15 views
9

In un ultimo post agosto sbzoom ha proposto una soluzione per rendere la primavera-dati-MongoDB multi-tenant:.Fare primavera-dati-mongodb multi-tenant

"Dovete fare il vostro proprio RepositoryFactoryBean Ecco l'esempio il Spring Data MongoDB Reference Docs. Dovresti ancora implementare il tuo MongoTemplate e ritardare o rimuovere la chiamata ensureIndexes(), ma dovresti riscrivere alcune classi per assicurarti che il tuo MongoTemplate venga chiamato al posto di Spring. "

Qualcuno ha implementato questo o qualcosa di simile?

risposta

13

Ci sono diversi modi per scuoiare il gatto qui. In sostanza, tutto si riduce a quale livello ti piacerebbe applicare la locazione.

Basics

L'approccio di base è quello di legare una sorta di chiave che identifica il cliente su una base per-thread, in modo che si può scoprire sul cliente il thread corrente di offerte di esecuzione con. Solitamente questo viene ottenuto popolando un ThreadLocal con alcune informazioni relative all'autenticazione, poiché in genere è possibile derivare il tenant dall'utente connesso.

Ora se questo è a posto ci sono alcune opzioni su dove applicare la conoscenza dell'inquilino. Mi permetta di illustrare brevemente le più comuni:

Multi-tenancy sul livello di database

Un modo per separare i dati per più client è quello di avere singoli database per ogni inquilino. Dati primaverili L'astrazione principale di MongoDB per questo è l'interfaccia MongoDBFactory. Il modo più semplice qui è quello di ignorare SimpleMongoDbFactory.getDb(String name) e chiamare il metodo genitore con il nome del database, ad es. arricchito dal prefisso inquilino o simili.

Multi-tenancy sul livello di raccolta

Un'altra opzione è quella di avere le collezioni specifiche inquilino, per esempio tramite prefisso o postfix. Questo meccanismo può essere sfruttato utilizzando il linguaggio Spring Expression (SpEl) nell'attributo collectionName dell'annotazione dell'annotazione . In primo luogo, esporre il prefisso inquilino attraverso un bean Spring:

@Component("tenantProvider") 
public class TenantProvider { 

    public String getTenantId() { 
    // … implement ThreadLocal lookup here 
    } 
} 

Quindi utilizzare SPEL nei vostri tipi di dominio @Document mappatura:

@Document(collectionName = "#{tenantProvider.getTenantId()}_accounts" 
public class Account { … } 

SPEL consente di fare riferimento a bean Spring per nome e eseguire i metodi su di essi . MongoTemplate (e quindi l'astrazione del repository in modo transitorio) utilizzerà i metadati di mappatura della classe del documento e il sottosistema di mappatura valuterà l'attributo collectionName per scoprire la collezione con cui interagire.

+1

Grazie mille per la pronta risposta di Oliver. Abbiamo già deciso di seguire il primo approccio, il livello di database. sbzoom aveva sollevato un problema: "MongoTemplate esegue il suo metodo ensureIndexes() dal suo costruttore, che chiama il database per assicurarsi che esistano indici annotati nel database.Il costruttore di MongoTemplate viene chiamato all'avvio di Spring, quindi non ho mai nemmeno la possibilità di impostare una variabile ThreadLocal. "Qual è il lavoro intorno a questo? – nicolasvdb

+1

@Oliver per multi-tenancy a livello di raccolta, ci sarebbe un problema che il Mongo gli indici non verrebbero creati all'avvio per le raccolte? Lo farebbe su richiesta? – robinhowlett

+0

Ma come funziona con '' 'MongoRepositories''', hanno bisogno di un' '' MongoTemplate''' al momento della creazione. mi piacerebbe avere qualche mappatura come '' '' '' per i miei scopi, ma come implementarla? – Zarathustra

2

Ho avuto un approccio simile a Oliver Gierke. Almeno a livello di database. https://github.com/Loki-Afro/multi-tenant-spring-mongodb Si dovrebbe essere in grado di fare cose come questa:

 MultiTenantMongoDbFactory.setDatabaseNameForCurrentThread("test"); 
     this.personRepository.save(createPerson("Phillip", "Wirth", ChronoUnit.YEARS.between(
       LocalDate.of(1992, Month.FEBRUARY, 3), 
       LocalDate.now()))); 

     System.out.println("data from test: " + this.personRepository.findAll()); 
//  okay? fine. - lets switch the database 
     MultiTenantMongoDbFactory.setDatabaseNameForCurrentThread("test666"); 

//  should be empty 
     System.out.println("data from test666: " + this.personRepository.findAll());