自定义Spring Data存储库bean名称以用于多个数据源

woe*_*ler 10 java spring mongodb spring-data spring-data-mongodb

我有一个项目利用Spring Data(在这个例子中是MongoDB)与具有相同模式的多个数据库进行交互.这意味着每个数据库使用相同的实体和存储库类.所以,例如:

public class Thing {
    private String id;
    private String name;
    private String type;
    // etc...  
}

public interface ThingRepository extends PagingAndSortingRepository<Thing, String> {
    List<Thing> findByName(String name);
}

@Configuration
@EnableMongoRepositories(basePackageClasses = { ThingRepository.class })
public MongoConfig extends AbstractMongoConfiguration {
    // Standard mongo config
}
Run Code Online (Sandbox Code Playgroud)

如果我连接到单个数据库,这可以正常工作,但是当我想同时连接到多个数据库时,事情会变得更复杂:

@Configuration
@EnableMongoRepositories(basePackageClasses = { ThingRepository.class },
    mongoTemplateRef = "mongoTemplateOne")
public MongoConfigOne extends AbstractMongoConfiguration {

    @Override
    @Bean(name = "mongoTemplateOne")
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(this.mongo(), "db_one");
    }

    // Remaining standard mongo config

}

@Configuration
@EnableMongoRepositories(basePackageClasses = { ThingRepository.class },
    mongoTemplateRef = "mongoTemplateTwo")
public MongoConfigTwo extends AbstractMongoConfiguration {

    @Override
    @Bean(name = "mongoTemplateTwo")
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(this.mongo(), "db_two");
    }

    // Remaining standard mongo config

}
Run Code Online (Sandbox Code Playgroud)

我可以使用不同的MongoTemplate实例创建同一存储库的多个实例,但我不知道引用和注入它们的正确方法.我希望能够将各个存储库实例注入不同的控制器,如下所示:

@Controller
@RequestMapping("/things/one/")
public class ThingOneController {
    @Resource private ThingRepository thingRepositoryOne;
    ...
}

@Controller
@RequestMapping("/things/two/")
public class ThingTwoController {
    @Resource private ThingRepository thingRepositoryTwo;
    ...
}
Run Code Online (Sandbox Code Playgroud)

这样的配置可能吗?我可以以某种方式控制实例化接口的bean名称,以便我可以用@Resource或引用它们@Autowired吗?

奖金问题:这可以通过自定义存储库工厂完成吗?

ale*_*xbt 11

创建您的存储库界面@NoRepositoryBean,我们将自己连接起来:

@NoRepositoryBean
public interface ModelMongoRepository extends MongoRepository<Model, String> {
}      
Run Code Online (Sandbox Code Playgroud)

然后,在@Configuration类中,使用实例化2个存储库bean MongoRepositoryFactoryBean.两个存储库都将返回相同的Spring Data Repository接口,但我们将为它们分配不同的MongoOperations(即:数据库详细信息):

@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());
    }
}
Run Code Online (Sandbox Code Playgroud)

您现在有2个具有不同@Qualifier名称的bean ,每个bean 都配置用于不同的数据库,并使用相同的模型.

您可以使用@Qualifier以下方法注入它

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

@Autowired
@Qualifier("two")
private ModelMongoRepository mongoRepositoryTwo;
Run Code Online (Sandbox Code Playgroud)

为简单起见,我对配置类中的值进行了硬编码,但您可以从application.properties/yml中的属性中注入它们.

编辑回答评论:

如果您想在不失去spring数据接口存储库优势的情况下创建自定义实现,那么这是修改.规格说:

通常有必要为一些存储库方法提供自定义实现.Spring Data存储库可以轻松地提供自定义存储库代码,并将其与通用CRUD抽象和查询方法功能集成.要使用自定义功能丰富存储库,首先要为自定义功能定义接口和实现.使用您提供的存储库接口来扩展自定义接口.找到类的最重要的一点是它的名称的Impl后缀与核心存储库接口相比(见下文).

创建一个新的界面,它在技术上与spring数据无关,旧的界面很好:

public interface CustomMethodsRepository {
    public void getById(Model model){
}
Run Code Online (Sandbox Code Playgroud)

让您的存储库接口扩展此新接口:

@NoRepositoryBean
public interface ModelMongoRepository extends MongoRepository<Model, String>, CustomMethodsRepository {
} 
Run Code Online (Sandbox Code Playgroud)

然后,创建实现类,它只实现非spring-data接口:

public class ModelMongoRepositoryImpl  implements CustomModelMongoRepository {
    private MongoOperations mongoOperations;

    public ModelMongoRepositoryImpl(MongoOperations mongoOperations) {
        this.mongoOperations = mongoOperations;
    }
    public void getById(Model model){
        System.out.println("test");
    }
}
Run Code Online (Sandbox Code Playgroud)

更改要添加的Java配置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();
}
Run Code Online (Sandbox Code Playgroud)

如果您没有通过Java配置手动连接存储库,则必须命名此实现ModelMongoRepositoryImpl以匹配接口ModelMongoRepository +"Impl".它将由春天自动处理.