自定义 Spring 数据存储库 bean 名称以用于多个数据源
我有一个项目,它利用 Spring Data(在本例中为 MongoDB)与具有相同架构的多个数据库进行交互.这意味着每个数据库都使用相同的实体和存储库类.所以,例如:
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 utilizes the same entity and repository classes. So, for example:
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
}
如果我要连接到单个数据库,这可以正常工作,但是当我想同时连接到多个数据库时,事情会变得更加复杂:
This works fine if I am connecting to a single database, but things get more complicated when I want to connect to more than one database at the same time:
@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
}
我可以使用不同的 MongoTemplate
实例创建同一个存储库的多个实例,但我不知道引用和注入它们的正确方法.我希望能够将各个存储库实例注入不同的控制器,如下所示:
I can create multiple instances of the same repository, using different MongoTemplate
instances, but I don't know the correct way to reference and inject them. I would like to be able to inject the individual repository instances into different controllers, like so:
@Controller
@RequestMapping("/things/one/")
public class ThingOneController {
@Resource private ThingRepository thingRepositoryOne;
...
}
@Controller
@RequestMapping("/things/two/")
public class ThingTwoController {
@Resource private ThingRepository thingRepositoryTwo;
...
}
这样的配置可能吗?我能否以某种方式控制实例化接口的 bean 名称,以便我可以使用 @Resource
或 @Autowired
引用它们?
Is a configuration like this possible? Can I somehow control the bean names of the instantiated interfaces so that I can reference them with @Resource
or @Autowired
?
额外问题:这也可以通过自定义存储库工厂来完成吗?
Bonus question: can this be accomplished with a custom repository factory as well?
推荐答案
使用 @NoRepositoryBean
创建你的存储库接口,我们会自己连接:
Create your repository interface with @NoRepositoryBean
, we'll wire it up ourself:
@NoRepositoryBean
public interface ModelMongoRepository extends MongoRepository<Model, String> {
}
然后,在 @Configuration
类中,使用 MongoRepositoryFactoryBean
实例化 2 个存储库 bean.两个存储库将返回相同的 Spring Data Repository 接口,但我们将为它们分配不同的 MongoOperations
(即:数据库详细信息):
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());
}
}
您现在有 2 个具有不同 @Qualifier
名称的 bean,每个都为不同的数据库配置,并使用相同的模型.
You now have 2 beans with distinct @Qualifier
names, each configured for different databases, and using the same model.
您可以使用 @Qualifier
注入它们:
You can inject them using @Qualifier
:
@Autowired
@Qualifier("one")
private ModelMongoRepository mongoRepositoryOne;
@Autowired
@Qualifier("two")
private ModelMongoRepository mongoRepositoryTwo;
为简单起见,我在配置类中硬编码了这些值,但您可以从 application.properties/yml 中的属性注入它们.
For simplicity, I've hard coded the values in the configuration class, but you can inject them from properties in application.properties/yml.
编辑回答评论:
如果你想创建一个自定义实现而不失去 Spring 数据接口存储库的好处,这里是修改.规格是这样说的:
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:
通常需要为少数人提供自定义实现存储库方法.Spring Data 存储库可让您轻松地提供自定义存储库代码并将其与通用 CRUD 集成抽象和查询方法功能.丰富存储库使用自定义功能,您首先定义一个接口和一个自定义功能的实现.使用存储库您提供的用于扩展自定义接口的接口.最多要找到的类的重要一点是 Impl 后缀它上面的名称与核心存储库接口相比(见下文).
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).
新建一个接口,技术上和spring数据无关,好旧的接口:
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 {
}
然后,创建您的实现类,它仅实现您的非 spring-data 接口:
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");
}
}
更改 Java 配置以添加 myFactory.setCustomImplementation(new ModelMongoRepositoryImpl());
:
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();
}
如果您没有通过 Java 配置手动连接存储库,则必须将此实现命名为 ModelMongoRepositoryImpl
以匹配接口 ModelMongoRepository +"Impl"
.并且会被spring自动处理.
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.
相关文章