如何为 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&lt;?&gt; type). 如果您从父界面扩展您的文档,这将不起作用。如果此接口在存储库签名 ReactiveMongoRepository&lt;iProductPrice, String&gt; 中声明,则即使是硬编码的集合名称也会被忽略 - @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 资源服务器?

已解决 - 如何为 Spring 中的 WebSocket 升级请求自定义 HTTP 响应?

如何为特定 url 启用 spring 安全会话管理

如何为 geom_hline 添加自定义图例

如何为objective-C中的类中的数组分配内存?