如何配置 spring-data-mongodb 以使用 2 个不同的 mongo 实例共享相同的文档模型
How to configure spring-data-mongodb to use 2 different mongo instances sharing the same document model
我在一家拥有 多个品牌 的公司工作,因此我们在一些 Mongo 数据库实例 上有几个 [= =63=]不同的主机 在每个品牌上为我们的客户持有相同的文档模型。 (结构相同,数据不同)
为了简单起见,假设我们有一个 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 配置,例如:
@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);
}
}
我 也 对 Orange 有类似的配置,它指向正确的 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 配置都指向此处的 相同型号包 。正确的。但我也尝试了其他方法,例如创建一个 BananaCustomer extends Customer,到它自己的 model.banana 包中,以及另一个 OrangeCustomer 将 Customer 扩展到其 model.orange 包中,同时指定 proper basePackageClasses 到 每个配置 。但是 都没有用 ,我最终得到了针对 相同数据库 .
的两个查询 运行
:(
在搜索官方 Spring-data-mongodb 文档数小时后,查看了数千行代码后,我 运行 别无选择: 似乎以前没有人做过我想要完成的事情。
除了,这里的这个人必须做同样的事情,但使用 JPA 而不是 mongodb:Link to article
嗯,虽然它仍然是 spring-数据,但它不适用于 mongodb。
所以这是我的问题:
¿如何明确地告诉每个存储库使用特定的mongo配置?
神奇的自动装配规则,除非它不起作用并且没有人理解魔法。
提前致谢。
好吧,我有一个非常详细的答案,但 Whosebug 抱怨看起来像垃圾邮件并且不允许我 post
完整答案仍然可用 Gist file here
底线是MongoRepository(接口)和模型对象必须放在同一个包裹。
我在一家拥有 多个品牌 的公司工作,因此我们在一些 Mongo 数据库实例 上有几个 [= =63=]不同的主机 在每个品牌上为我们的客户持有相同的文档模型。 (结构相同,数据不同)
为了简单起见,假设我们有一个 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 配置,例如:
@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);
}
}
我 也 对 Orange 有类似的配置,它指向正确的 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 配置都指向此处的 相同型号包 。正确的。但我也尝试了其他方法,例如创建一个 BananaCustomer extends Customer,到它自己的 model.banana 包中,以及另一个 OrangeCustomer 将 Customer 扩展到其 model.orange 包中,同时指定 proper basePackageClasses 到 每个配置 。但是 都没有用 ,我最终得到了针对 相同数据库 .
的两个查询 运行:(
在搜索官方 Spring-data-mongodb 文档数小时后,查看了数千行代码后,我 运行 别无选择: 似乎以前没有人做过我想要完成的事情。 除了,这里的这个人必须做同样的事情,但使用 JPA 而不是 mongodb:Link to article
嗯,虽然它仍然是 spring-数据,但它不适用于 mongodb。
所以这是我的问题:
¿如何明确地告诉每个存储库使用特定的mongo配置?
神奇的自动装配规则,除非它不起作用并且没有人理解魔法。
提前致谢。
好吧,我有一个非常详细的答案,但 Whosebug 抱怨看起来像垃圾邮件并且不允许我 post
完整答案仍然可用 Gist file here
底线是MongoRepository(接口)和模型对象必须放在同一个包裹。