简体   繁体   中英

How to configure spring-data-mongodb to use 2 different mongo instances sharing the same document model

I work for a company that has multiple brands , therefore we have a couple MongoDB instances on some different hosts holding the same document model for our Customer on each of these brands. (Same structure, not same data)

For the sake of simplicity let's say we have an Orange brand, with database instance serving on port 27017 and Banana brand with database instance serving on port 27018

Currently I'm developing a fraud detection service which is required to connect to all databases and analyze all the customers' behavior together regardless of the brand.

So my "model" has a shared entity for Customer , annotated with @Document (org.springframework.data.mongodb.core.mapping.Document)

Next thing I have is two MongoRepositories such as:

public interface BananaRepository extends MongoRepository<Customer, String>

    List<Customer> findAllByEmail(String email);

public interface OrangeRepository extends MongoRepository<Customer, String>

    List<Customer> findAllByEmail(String email);

With some stub method for finding customers by Id, Email, and so on. Spring is responsible for generating all implementation classes for such interfaces (pretty standard spring stuff)

In order to hint each of these repositories to connect to the right mongodb instance, I need two Mongo Config such as:


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

I also have similar config for Orange which points to the proper mongo instance.

Then I have my service like this:

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

    }

Notice that I'm calling both repositories and then collecting back the results into one single list

What I would expect to happen is that each repository would connect to its corresponding mongo instance and query for the customer by its email. But this don't happened . I always got the query executed against the same mongo instance. But in the database log I can see both connections being made by spring. It just uses one connection to run the queries for both repositories.

This is not surprising as both Mongo Config points to the same model package here. Right. But I also tried other approaches such as creating a BananaCustomer extends Customer , into its own model.banana package, and another OrangeCustomer extends Customer into its model.orange package, along with specifying the proper basePackageClasses into each config . But that neither worked , I've ended up getting both queries run against the same database .

:(

After scavenging official Spring-data-mongodb documentation for hours, and looking throughout thousands of lines of code here and there, I've run out of options: seems like nobody have done what I'm trying to accomplish before . Except for this guy here that had to do the same thing but using JPA instead of mongodb: Link to article

Well, while it's still spring-data it't not for mongodb.

So here is my question:

¿How can I explicitly tell each repository to use a specific mongo config ?

Magical autowiring rules, except when it doesn't work and nobody understands the magic .

Thanks in advance.

Well, I had a very detailed answer but StackOverflow complained about looking like spam and didn't allow me to post

The full answer is still available as a Gist file here

The bottom line is that both MongoRepository (interface) and the model object must be placed in the same package .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM