Customizing Spring Data repository bean names for use with multiple data sources

后端 未结 2 2034
死守一世寂寞
死守一世寂寞 2021-01-02 03:39

I have a project that utilizes Spring Data (MongoDB in this instance) to interact with multiple databases with the same schema. What this means is that each database utiliz

相关标签:
2条回答
  • 2021-01-02 03:51

    For a general @Repository you can just add (value="someDao") to name the created Bean, if the MongoRepository extends Repository this should work.

    0 讨论(0)
  • 2021-01-02 03:55

    Create your repository interface with @NoRepositoryBean, we'll wire it up ourself:

    @NoRepositoryBean
    public interface ModelMongoRepository extends MongoRepository<Model, String> {
    }      
    

    Then, in a @Configuration class, instantiate the 2 repository beans using MongoRepositoryFactoryBean. Both repositories will return the same Spring Data Repository interface, but we'll assign them different MongoOperations (ie: database details):

    @Configuration
    @EnableMongoRepositories
    public class MongoConfiguration {
    
        @Bean
        @Qualifier("one")
        public ModelMongoRepository modelMongoRepositoryOne() throws DataAccessException, Exception {
            MongoRepositoryFactoryBean<ModelMongoRepository, Model, String> myFactory = new MongoRepositoryFactoryBean<ModelMongoRepository, Model, String>();
            myFactory.setRepositoryInterface(ModelMongoRepository.class);
            myFactory.setMongoOperations(createMongoOperations("hostname1", 21979, "dbName1", "username1", "password1"));
            myFactory.afterPropertiesSet();
            return myFactory.getObject();
        }
    
        @Bean
        @Qualifier("two")
        public ModelMongoRepository modelMongoRepositoryTwo() throws DataAccessException, Exception {
            MongoRepositoryFactoryBean<ModelMongoRepository, Model, String> myFactory = new MongoRepositoryFactoryBean<ModelMongoRepository, Model, String>();
            myFactory.setRepositoryInterface(ModelMongoRepository.class);
            myFactory.setMongoOperations(createMongoOperations("hostname2", 21990, "dbName2", "username2", "password2"));
            myFactory.afterPropertiesSet();
            return myFactory.getObject();
        }
    
        private MongoOperations createMongoOperations(String hostname, int port, String dbName, String user, String pwd) throws DataAccessException, Exception {
            MongoCredential mongoCredentials = MongoCredential.createScramSha1Credential(user, dbName, pwd.toCharArray());
            MongoClient mongoClient = new MongoClient(new ServerAddress(hostname, port), Arrays.asList(mongoCredentials));
            Mongo mongo = new SimpleMongoDbFactory(mongoClient, dbName).getDb().getMongo();
            return new MongoTemplate(mongo, dbName);
        }
        //or this one if you have a connection string
        private MongoOperations createMongoOperations(String dbConnection) throws DataAccessException, Exception {
            MongoClientURI mongoClientURI = new MongoClientURI(dbConnection);
            MongoClient mongoClient = new MongoClient(mongoClientURI);
            Mongo mongo = new SimpleMongoDbFactory(mongoClient, mongoClientURI.getDatabase()).getDb().getMongo();
            return new MongoTemplate(mongo, mongoClientURI.getDatabase());
        }
    }
    

    You now have 2 beans with distinct @Qualifier names, each configured for different databases, and using the same model.

    You can inject them using @Qualifier :

    @Autowired
    @Qualifier("one")
    private ModelMongoRepository mongoRepositoryOne;
    
    @Autowired
    @Qualifier("two")
    private ModelMongoRepository mongoRepositoryTwo;
    

    For simplicity, I've hard coded the values in the configuration class, but you can inject them from properties in application.properties/yml.

    EDIT to answer comments:

    Here's the modification if you want to create a custom implementation without loosing the benefits of spring data interface repositories. the specs says this:

    Often it is necessary to provide a custom implementation for a few repository methods. Spring Data repositories easily allow you to provide custom repository code and integrate it with generic CRUD abstraction and query method functionality. To enrich a repository with custom functionality you first define an interface and an implementation for the custom functionality. Use the repository interface you provided to extend the custom interface. The most important bit for the class to be found is the Impl postfix of the name on it compared to the core repository interface (see below).

    Create a new interface, which has technically nothing to do with spring data, good old interface:

    public interface CustomMethodsRepository {
        public void getById(Model model){
    }
    

    Have your repository interface extends this new interface:

    @NoRepositoryBean
    public interface ModelMongoRepository extends MongoRepository<Model, String>, CustomMethodsRepository {
    } 
    

    Then, create your implementation class, which only implements your non-spring-data interface:

    public class ModelMongoRepositoryImpl  implements CustomModelMongoRepository {
        private MongoOperations mongoOperations;
    
        public ModelMongoRepositoryImpl(MongoOperations mongoOperations) {
            this.mongoOperations = mongoOperations;
        }
        public void getById(Model model){
            System.out.println("test");
        }
    }
    

    Change the Java configuration to add myFactory.setCustomImplementation(new ModelMongoRepositoryImpl()); :

    @Bean
    @Qualifier("one")
    public ModelMongoRepository modelMongoRepositoryOne() throws DataAccessException, Exception {
        MongoRepositoryFactoryBean<ModelMongoRepository, Model, String> myFactory = new MongoRepositoryFactoryBean<ModelMongoRepository, Model, String>();
        MongoOperations mongoOperations = createMongoOperations("hostname1", 21979, "dbName1", "usdername1", "password1");
        myFactory.setCustomImplementation(new ModelMongoRepositoryImpl(mongoOperations));
        myFactory.setRepositoryInterface(ModelMongoRepository.class);
        myFactory.setMongoOperations(mongoOperations);
    
        myFactory.afterPropertiesSet();
        return myFactory.getObject();
    }
    

    If you were not wiring the repository manually through Java config, this implementation would HAVE to be named ModelMongoRepositoryImpl to match the interface ModelMongoRepository +"Impl". And it would be handled automatically by spring.

    0 讨论(0)
提交回复
热议问题