2016-07-25 2 views
0

ОК, поэтому у меня есть интересная проблема. Я использую java/maven/spring-boot/cassandra ... и я пытаюсь создать динамическое создание конфигурации Mapper, которую они используют. I.E.доступ к дочерней константе в родительском классе в java

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

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

Теперь для того, чтобы использовать это, я должен был бы явно сказать ...

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

, очевидно, замена (DB) с моим классом дб. Это отличная модель, но я столкнулся с проблемой повторения кода. В моей базе данных Cassandra есть 2 пространства ключей, оба пространства ключей имеют одинаковые таблицы с одинаковыми столбцами в таблицах (это не мой выбор, это абсолютно необходимо, согласно моей компании). Поэтому, когда мне нужно получить доступ к одной или другой на основе представления формы становится беспорядок дублированного кода, например:

//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 ""; 
} 

Я пытался использовать такие методы, как ...

Class cls = Class.forName(STRING_INPUT_VARIABLE_HERE); 

и это работает нормально для базовых классов, но при попытке использовать материал Accessor он больше не работает, потому что Accessors должны быть интерфейсами, поэтому, когда вы выполняете Class cls, он больше не является интерфейсом.

Я пытаюсь найти любое другое решение о том, как динамически выполнять эту работу и не иметь дублирующий код для каждого возможного клиента. Каждый клиент будет иметь собственное пространство имен в Кассандре, с теми же таблицами, что и все остальные.

Я не могу изменить модель базы данных, это необходимо в соответствии с компанией. С PHP это очень просто, так как он не заботится о типажей столько же, я могу легко сделать ...

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

и пуф У меня динамический класс, но проблема, я бегу в это тип спецификация, где я должен явно сказать ...

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

Я надеюсь, что это делает чувство, я не могу себе представить, что я первый человек, чтобы иметь эту проблему, но я не могу найти какие-либо решения в Интернете. Поэтому я действительно надеюсь, что кто-то знает, как я могу добиться этого, не дублируя ту же логику для каждого отдельного пространства ключей, которое у нас есть. Это делает код незащищенным и излишне длинным.

Благодарим вас за любую помощь, которую вы можете предложить.

ответ

1

Не указывайте пространство ключей в классах моделей и вместо этого используйте так называемый шаблон «session per keyspace».

Ваша модель класса будет выглядеть следующим образом (обратите внимание, что пространство ключей остается неопределенным):

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

Ваш код инициализации будет иметь что-то вроде этого:

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 

Вы бы затем хранить mappers объект и сделать его доступным через инъекцию зависимостей другим компонентам вашего приложения.

Наконец, ваша служба REST будет выглядеть следующим образом:

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); 
    } 
} 

Обратите внимание, как впрыскивается объект mappers.

Small nit: Я бы назвал ваш класс User в единственном числе, а не Users (во множественном числе).

+0

чистый genuis !!! большое вам спасибо, это просто красиво! –

+0

У меня был последний вопрос для ya .... как бы вы справились с аксессуарами? в аксессуре у вас есть тег @Accessor над вашим интерфейсом, тогда вам нужно написать запрос, который вы используете, например .. atQuery («Выбрать * из keyspace_first_client.users WHERE email =: email») Результат getByEmail (atParam (atParam (atParam) «email») String email); Где вам нужно жестко закодировать пространство ключей в запросе. Должен ли я просто делать аналогичную модель карт для аксессуаров, вы думаете, что просто запустить ее на правильной карте клиента будет работать без определения пространства ключей в нотации запроса? Большое вам спасибо, вы очень полезны –

+1

Да, аксессоры работают одинаково: если вы не укажете пространство ключей для запроса, они будут использовать пространство ключей сеанса. Таким образом, вы в конечном итоге используете один и тот же шаблон (один сеанс для каждого клиентского/ключевого пространства). – adutra

Смежные вопросы