如何为 Spring Data 中的类配置 MongoDb 集合名称
Posted
技术标签:
【中文标题】如何为 Spring Data 中的类配置 MongoDb 集合名称【英文标题】:How To Configure MongoDb Collection Name For a Class in Spring Data 【发布时间】:2012-08-29 17:38:25 【问题描述】:我的 MongoDB 数据库中有一个名为 Products
的集合,在我的 Java 代码中由接口 IProductPrice
表示。以下存储库声明导致 Spring Date 查找集合 db.collection: Intelliprice.iProductPrice
。
我希望它使用外部配置将其配置为查看db.collection: Intelliprice.Products
,而不是在IProductPrice
上放置@Collection(..)
注释。这可能吗?我该怎么做?
public interface ProductsRepository extends
MongoRepository<IProductPrice, String>
【问题讨论】:
【参考方案1】:您目前可以实现此目的的唯一方法是使用@Document
注释您的域类,使用collection
属性来定义此类的集合实例的名称应被持久化。
但是,有一个JIRA issue open 建议添加一个可插入的命名策略来配置以更全局的方式处理类、集合和属性名称的方式。随意评论您的用例并投票。
【讨论】:
谢谢,我知道@Document 注释并且可能最终会使用它。我基本上想从实际类中外部化配置。您链接到的 JIRA 问题正在讨论命名策略,但仍然建议使用自定义名称的注释。collection
属性支持 SpEL,因此您可以在其他 Spring bean 上调用任意方法来计算集合名称,例如,如果您已将组件注册为 bean
,则使用 ##bean.someMethod(T(your.fully.qualified.Type))
提供方法someMethod(Class<?> type)
.
如果您从父界面扩展您的文档,这将不起作用。如果此接口在存储库签名 ReactiveMongoRepository<iProductPrice, String>
中声明,则即使是硬编码的集合名称也会被忽略 - @Document(collection = "specific_collection_name")
。收藏将是iproductprice
@Zon 在这种情况下,您需要使用 sepl 方法在基类/父类级别设置集合名称。 This 解决方案使用简单。
我已经尝试过 SPEL,但是您将拥有一个所有后代的集合名称。如果您调用某个方法或将参数传递给原型 bean - 无论如何,集合名称仅在创建 bean 时设置一次。我什至尝试过动态设置注释值——这也没有帮助。剩下的唯一选择 - 将 Spring Data 存储库重写为 MongoTemplate,允许通过查询传递集合名称。【参考方案2】:
使用上面 Oliver Gierke 的回答, 在一个需要为一个实体创建多个集合的项目上工作,我想使用 spring 存储库,并且需要在使用存储库之前指定要使用的实体。
我设法使用此系统按需修改存储库集合名称,它使用 SPeL。不过,您一次只能处理 1 个集合。
域对象
@Document(collection = "#personRepository.getCollectionName()")
public class Person
默认 Spring 存储库:
public interface PersonRepository
extends MongoRepository<Person, String>, PersonRepositoryCustom
自定义存储库接口:
public interface PersonRepositoryCustom
String getCollectionName();
void setCollectionName(String collectionName);
实施:
public class PersonRepositoryImpl implements PersonRepositoryCustom
private static String collectionName = "Person";
@Override
public String getCollectionName()
return collectionName;
@Override
public void setCollectionName(String collectionName)
this.collectionName = collectionName;
使用它:
@Autowired
PersonRepository personRepository;
public void testRetrievePeopleFrom2SeparateCollectionsWithSpringRepo()
List<Person> people = new ArrayList<>();
personRepository.setCollectionName("collectionA");
people.addAll(personRepository.findAll());
personDocumentRepository.setCollectionName("collectionB");
people.addAll(personRepository.findAll());
Assert.assertEquals(4, people.size());
否则,如果您需要使用配置变量,您可以使用类似的东西吗? source
@Value("#systemProperties['pop3.port'] ?: 25")
【讨论】:
这个没有测试过,也不是很干净,但是+1只是为了有创意:) 似乎您将“上下文”信息保存在可能在各个地方自动连接的存储库中。我猜这个解决方案不是线程安全的。 @thanosa75 你是对的,我只是在重用该解决方案,并认为拥有一个始终提供集合名称的存储库会更安全:而不是 repo.findAll() > repo。 findAll("collectionName") 。但我不知道如何优雅地做到这一点(而不是重新创建一个重用 mongo 模板的类,并始终在运行请求之前设置集合名称) 这给出了循环依赖错误,当我从 Person bean 中删除 #notificationRepository.getCollectionName() 时,它得到了解决 发现了我的问题,这是一个项目问题,因为我正在创建一个基于全新 MongoMappingContext 而不是使用提供的 MongoMappingContext 的自定义 MongoTemplate。【参考方案3】:我可以添加的唯一注释是您必须在 bean 名称中添加 @ 前缀:
collection = "#@beanName.method()"
为 bean factory 注入 bean:
@Document(collection = "#@configRepositoryCustom.getCollectionName()")
public class Config
我很难弄明白..
完整示例:
@Document(collection = "#@configRepositoryCustom.getCollectionName()")
public class Config implements Serializable
@Id
private String uuid;
private String profile;
private String domain;
private String label;
private Map<String, Object> data;
// get/set
public interface ConfigRepositoryCustom
String getCollectionName();
void setCollectionName(String collectionName);
@Component("configRepositoryCustom")
public class ConfigRepositoryCustomImpl implements ConfigRepositoryCustom
private static String collectionName = "config";
@Override
public String getCollectionName()
return collectionName;
@Override
public void setCollectionName(String collectionName)
this.collectionName = collectionName;
@Repository("configurations")
public interface ConfigurationRepository extends MongoRepository<Config, String>, ConfigRepositoryCustom
public Optional<Config> findOneByUuid(String Uuid);
public Optional<Config> findOneByProfileAndDomain(String profile, String domain);
在 serviceImpl 中的使用:
@Service
public class ConfigrationServiceImpl implements ConfigrationService
@Autowired
private ConfigRepositoryCustom configRepositoryCustom;
@Override
public Config create(Config configuration)
configRepositoryCustom.setCollectionName( configuration.getDomain() ); // set the collection name that comes in my example in class member 'domain'
Config configDB = configurationRepository.save(configuration);
return configDB;
【讨论】:
我也只能使用 '@' bean 前缀来完成这项工作。不确定该语法,因为在@Oliver Drotbohm|s 中提到的Jira Issue 中也没有建议使用该语法,否则解决方案也被记录在案。 在here 中记录了 bean 引用的“@”前缀的用法。感谢马克hint。 出于某种原因,这对我有用,但前提是 bean 名称的第一个字母是小写的。所以不是#@ActualBeanName.method()"
,它只有在我使用#@actualBeanName.method()"
时才有效【参考方案4】:
我在 SpEL 中使用静态类和方法;
public class CollectionNameHolder
private static final ThreadLocal<String> collectionNameThreadLocal = new ThreadLocal<>();
public static String get()
String collectionName = collectionNameThreadLocal.get();
if(collectionName == null)
collectionName = DataCenterApiConstant.APP_WECHAT_DOCTOR_PATIENT_COLLECTION_NAME;
collectionNameThreadLocal.set(collectionName);
return collectionName;
public static void set(String collectionName)
collectionNameThreadLocal.set(collectionName);
public static void reset()
collectionNameThreadLocal.remove();
在实体类中,@Document(collection = "#T(com.test.data.CollectionNameHolder).get()")
然后,使用
CollectionNameHolder.set("testx_"+pageNum)
在服务中,并且
CollectionNameHolder.reset();
希望对你有帮助。
【讨论】:
【参考方案5】:有点晚了, 但我发现您可以在 spring-boot 中动态设置 mongo 集合名称,直接访问应用程序配置。
@Document(collection = "#@environment.getProperty('configuration.property.key')")
public class DomainModel ...
我怀疑你可以这样设置任何注释属性。
【讨论】:
以上是关于如何为 Spring Data 中的类配置 MongoDb 集合名称的主要内容,如果未能解决你的问题,请参考以下文章
如何为 Spring Boot 应用程序外部化 data.sql
如何为 spring webflux 应用程序配置 oauth2 资源服务器?