2016-07-25 21 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类替代(DB)。 这是一个很好的模型,但我遇到了代码重复的问题。我的Cassandra数据库有两个密钥空间,两个密钥空间都有完全相同的表格,并且表格中有完全相同的列,(这不是我的选择,这是我公司绝对必须具备的)。所以,当我需要根据表单提交访问一个或另一个成为重复的代码,例如一个烂摊子:

//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时它不再起作用,因为访问器必须是接口,所以当您执行Class cls时,它不再是接口。

我想找到任何其他解决方案如何动态地有这项工作,而不必为每个可能的客户端重复代码。每个客户端都会在Cassandra中拥有自己的名称空间,并且与其他所有的名称空间完全相同。

我不能更改数据库模型,这是一个必须根据公司。 使用PHP,这是非常简单的,因为它不关心类型转换为多,我可以很容易做到......

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

和噗我有一个动态类,但我遇到的问题是类型规格在这里我必须明确地说...

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

我希望这是决策意识,我无法想象我是有这个问题的第一人,但我不能找到任何解决方案。所以我真的希望有人知道如何在不重复每个单个密钥空间的完全相同的逻辑的情况下实现这一目标。它使代码无法维护并且不必要的长。

非常感谢您提供的任何帮助。

回答

1

请勿在您的模型类中指定密钥空间,而是使用所谓的“每个密钥空间会话”模式。

模型类是这样的(注意,密钥空间是左未定义):

@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对象是如何注入。

小尼特:我会用单数来命名您的班级User,而不是Users(复数形式)。

+0

纯genuis!非常感谢你,那简直太美了! –

+0

我还有最后一个问题,你会如何处理访问器?在访问器中,你的接口上方有@Accessor标签,那么你需要编写你正在使用的查询.. atQuery(“Select * from keyspace_first_client.users WHERE email =:email”) 结果 getByEmail(atParam( “email”)字符串电子邮件); 您必须在查询中对密钥空间进行硬编码的位置。我是否应该为访问者做一个类似的地图模型,你认为只要在正确的客户端地图上运行它就可以工作,而不需要在查询符号中定义一个密钥空间?非常感谢,您非常乐于助人 –

+1

是的,访问者的工作方式相同:如果您没有为查询指定密钥空间,他们将使用会话的密钥空间。所以基本上你最终会使用相同的模式(每个客户端/密钥空间一个会话)。 – adutra