如何将spring-data-mongodb配置为使用2个共享同一文档模型的不同mongo实例

时间:2019-01-19 19:25:49

标签: java mongodb spring-data-mongodb

我为一家拥有多个品牌的公司工作,因此我们在一些不同主机上拥有几个 MongoDB实例,它们拥有相同的文档模型为我们的每个品牌的客户提供服务。 (相同的结构,不同的数据)

为简单起见,我们假设我们有一个 Orange 品牌,其中数据库实例在端口 27017 Banana 品牌上具有数据库实例在端口 27018

上投放

当前,我正在开发欺诈检测服务,该服务需要连接到所有数据库,并一起分析所有客户的行为,而不管其品牌是什么。

因此,我的“模型”具有客户共享实体,并用 @Document (org.springframework.data.mongodb.core .mapping.Document)

我接下来要拥有的是两个Mongo存储库,例如:

public interface BananaRepository extends MongoRepository<Customer, String>

    List<Customer> findAllByEmail(String email);

public interface OrangeRepository extends MongoRepository<Customer, String>

    List<Customer> findAllByEmail(String email);

使用一些存根方法来通过ID,电子邮件等查找客户。 Spring负责为此类接口生成所有实现类(相当标准的Spring东西)

为了暗示这些存储库中的每一个都连接到正确的mongodb实例,我需要两个Mongo Config,例如:


@Configuration
@EnableMongoRepositories(basePackageClasses = {Customer.class})
public class BananaConfig extends AbstractMongoConfiguration {

    @Value("${database.mongodb.banana.username:}")
    private String username;
    @Value("${database.mongodb.banana.database}")
    private String database;
    @Value("${database.mongodb.banana.password:}")
    private String password;
    @Value("${database.mongodb.banana.uri}")
    private String mongoUri;


    @Override
    protected Collection<String> getMappingBasePackages() {
        return Collections.singletonList("com.acme.model");
    }

    @Override
    protected String getDatabaseName() {
        return this.database;
    }

    @Override
    @Bean(name="bananaClient")
    public MongoClient mongoClient() {
        final String authString;

        //todo: Use MongoCredential
        //todo: Use ServerAddress
        //(See https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories) 10.3.4
        if ( valueIsPresent(username) ||valueIsPresent(password)) {
            authString = String.format("%s:%s@", username, password);
        } else {
            authString = "";
        }
        String conecctionString = "mongodb://" + authString + mongoUri + "/" + database;

        System.out.println("Going to connect to: " + conecctionString);

        return new MongoClient(new MongoClientURI(conecctionString, builder()
                .connectTimeout(5000)
                .socketTimeout(8000)
                .readPreference(ReadPreference.secondaryPreferred())
                .writeConcern(ACKNOWLEDGED)));

    }

    @Bean(name = "bananaTemplate")
    public MongoTemplate mongoTemplate(@Qualifier("bananaFactory") MongoDbFactory mongoFactory) {
        return new MongoTemplate(mongoFactory);
    }

    @Bean(name = "bananaFactory")
    public MongoDbFactory mongoFactory() {
        return new SimpleMongoDbFactory(mongoClient(),
                getDatabaseName());
    }



    private static int sizeOfValue(String value){
        if (value == null) return 0;
        return value.length();
    }
    private static boolean valueIsMissing(String value){
        return sizeOfValue(value) == 0;
    }
    private static boolean valueIsPresent(String value){
        return ! valueIsMissing(value);
    }
}

具有与橙色相似的配置,该配置指向正确的mongo实例。

然后我将得到这样的服务:

    public List<? extends Customer> findAllByEmail(String email) {
        return  Stream.concat(
                bananaRepository.findAllByEmail(email).stream(),
                orangeRepository.findAllByEmail(email).stream())
                .collect(Collectors.toList());

    }

通知,我正在呼叫两个存储库,然后将结果收集回一个列表

我希望发生的是,每个存储库将连接到其对应的mongo实例,并通过其电子邮件查询客户。 但这没有发生。我总是对同一个mongo实例执行查询。 但是在数据库日志中,我可以看到两个连接都是在spring之前建立的。 它仅使用一个连接来运行两个存储库的查询。

这并不奇怪,因为Mongo Config都在此处指向相同模型包。对。但是我还尝试了其他方法,例如将 BananaCustomer扩展Customer 放入其自己的 model.banana 包中,以及将另一个 OrangeCustomer扩展Customer 放入其包中 model.orange 包,并在每个配置中指定适当 basePackageClasses 。但这均无效,我最终使两个查询都针对相同数据库运行。

  

:(

在将Spring-data-mongodb官方文档整理了几个小时,并在各处查找数千行代码后,我用尽了所有选项:似乎没有人完成了我要完成的工作之前除了,这里的这个人必须做相同的事情,但使用JPA代替mongodb:Link to article

好吧,尽管它仍然是spring-data,但不是mongodb的。

所以这是我的问题:

我如何明确告诉每个存储库使用特定的mongo配置

神奇的自动装配规则,除非它不起作用并且没有人了解魔术

先谢谢了。

1 个答案:

答案 0 :(得分:0)

嗯,我有一个非常详细的答案,但是StackOverflow抱怨看起来像垃圾邮件,并且不允许我发布

完整答案仍然可以作为Gist file here

获得

最重要的是, MongoRepository (界面)和模型对象都必须放在相同的包中。