2016-07-25 24 views
0

OK, also habe ich ein interessantes Problem. Ich benutze Java/Maven/Spring-Boot/Cassandra ... und ich versuche eine dynamische Instanziierung der Mapper-Setup, die sie verwenden. I.E.Zugriff auf untergeordnete Konstante in der übergeordneten Klasse in Java

//Users.java 
import com.datastax.driver.mapping.annotations.Table; 

@Table(keyspace="mykeyspace", name="users") 
public class Users { 
    @PartitionKey 
    public UUID id; 
    //... 
} 

Nun, um dies zu verwenden würde ich ausdrücklich sagen ...

Users user = (DB).mapper(Users.class); 

offensichtlich ersetzen (DB) mit meiner db-Klasse. Das ist ein großartiges Modell, aber ich stoße auf das Problem der Code-Wiederholung. Meine Cassandra-Datenbank hat 2 Schlüsselräume, beide Schlüsselräume haben die exakt gleichen Tabellen mit den exakt gleichen Spalten in den Tabellen (dies ist nicht meine Wahl, dies ist ein absolutes Muss, entsprechend meiner Firma). Also, wenn ich brauche einen für den Zugriff auf oder die andere basiert auf einer Formulareinreichung wird es ein Durcheinander von duplizierten Code, Beispiel:

//myWebController.java 
import ...; 

@RestController 
public class MyRestController { 

@RequestMapping(value="/orders", method=RequestMethod.POST) 
public string getOrders(...) { 
    if(Objects.equals(client, "first_client_name") { 
     //do all the things to get first keyspace objects like.... 
     FirstClientUsers users = (db).Mapper(FirstClientUsers.class); 
     //... 
    } else if(Objects.equals(client, "second_client_name") { 
     SecondClientUsers users = (db).Mapper(SecondClientUsers.class); 
     //.... 
    } 
    return ""; 
} 

Ich habe versucht, wie Methoden zu verwenden ...

Class cls = Class.forName(STRING_INPUT_VARIABLE_HERE); 

und das funktioniert gut für Basisklassen, aber wenn Sie versuchen, den Accessor-Kram zu verwenden, funktioniert es nicht mehr, da Accessoren Interfaces sein müssen. Wenn Sie also Class-Cls machen, ist es keine Schnittstelle mehr.

Ich versuche, eine andere Lösung zu finden, wie dies dynamisch funktioniert und nicht für jeden möglichen Client doppelten Code haben muss. Jeder Client hat seinen eigenen Namensraum in Cassandra, mit genau denselben Tabellen wie alle anderen.

Ich kann das Datenbankmodell nicht ändern, dies ist ein Muss für das Unternehmen. Mit PHP ist dies sehr einfach, da es nicht so viel über eine Schublade gesteckt schert, was ich tun kann leicht ...

function getData($name) { 
    $className = $name . 'Accessor'; 
    $class = new $className(); 
} 

und puh habe ich eine dynamische Klasse, aber das Problem, das ich in laufende bin ist der Typ Spezifikation, wo ich ausdrücklich zu sagen haben ...

FirstClientUsers users = new FirstClientUsers(); 
//or even 
FirstClientUsers users = Class.forName("FirstClientUsers"); 

ich hoffe, dass dieser Sinn macht, kann ich mir nicht vorstellen, dass ich die erste Person bin, dieses Problem zu haben, aber ich online keine Lösungen finden können. Also hoffe ich wirklich, dass jemand weiß, wie ich das erreichen kann, ohne die exakt gleiche Logik für jeden einzelnen Schlüsselraum, den wir haben, zu kopieren. Es macht den Code nicht wartbar und unnötig lang.

Vielen Dank im Voraus für jede Hilfe, die Sie anbieten können.

Antwort

1

Geben Sie nicht den Schlüsselbereich in Ihren Modellklassen an und verwenden Sie stattdessen das Muster "Sitzung pro Schlüsselbereich".

Ihre Modellklasse würde wie folgt aussehen (beachten Sie, dass die Schlüsselraum nicht definiert ist):

@Table(name = "users") 
public class Users { 
    @PartitionKey 
    public UUID id; 
    //... 
} 

Ihr Initialisierungscode so etwas wie dieses haben würde:

Map<String, Mapper<Users>> mappers = new ConcurrentHashMap<String, Mapper<Users>>(); 

Cluster cluster = ...; 

Session firstClientSession = cluster.connect("keyspace_first_client"); 
Session secondClientSession = cluster.connect("keyspace_second_client"); 

MappingManager firstClientManager = new MappingManager(firstClientSession); 
MappingManager secondClientManager = new MappingManager(secondClientSession); 

mappers.put("first_client", firstClientManager.mapper(Users.class)); 
mappers.put("second_client", secondClientManager.mapper(Users.class)); 

// etc. for all clients 

Sie würden dann speichern Sie die mappers Objekt und stellen Sie es über die Abhängigkeitsinjektion für andere Komponenten in Ihrer Anwendung bereit.

Schließlich würde Ihre REST-Service wie folgt aussehen:

import ... 

@RestController 
public class MyRestController { 

    @javax.inject.Inject 
    private Map<String, Mapper<Users>> mappers; 

    @RequestMapping(value = "/orders", method = RequestMethod.POST) 
    public string getOrders(...) { 
     Mapper<Users> usersMapper = getUsersMapperForClient(client); 
     // process the request with the right client's mapper 
    } 

    private Mapper<Users> getUsersMapperForClient(String client) { 
     if (mappers.containsKey(client)) 
      return mappers.get(client); 
     throw new RuntimeException("Unknown client: " + client); 
    } 
} 

Hinweis, wie die mappers Objekt injiziert wird.

Kleine Nit: Ich würde Ihre Klasse User im Singular statt Users (im Plural) nennen.

+0

pure genuis !!! Vielen Dank, das ist einfach wunderschön! –

+0

Ich hatte eine letzte Frage für dich .... wie würdest du mit den Accessoren umgehen? im Accessor haben Sie das @Accessor-Tag über Ihrer Schnittstelle, dann müssen Sie die Abfrage, die Sie verwenden, wie schreiben .. atQuery ("Select * von keyspace_first_client.users WHERE email =: email") Ergebnis getByEmail (atParam ("email") String email); Hier müssen Sie den Schlüsselbereich in der Abfrage fest codieren. Sollte ich nur ein ähnliches Kartenmodell für Accessoren verwenden, glauben Sie, es einfach auf der richtigen Client-Map auszuführen, ohne einen Schlüsselbereich in der Abfrage-Notation zu definieren? Vielen Dank, Sie sind extrem hilfreich –

+1

Ja, Accessoren funktionieren auf die gleiche Weise: Wenn Sie keinen Schlüsselbereich für die Abfrage angeben, verwenden sie den Schlüsselbereich der Sitzung. Im Grunde verwenden Sie das gleiche Muster (eine Sitzung für jeden Client/Schlüsselbereich). – adutra