【问题标题】:How To Configure MongoDb Collection Name For a Class in Spring Data如何为 Spring Data 中的类配置 MongoDb 集合名称
【发布时间】: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> {
}

【问题讨论】:

    标签: java spring mongodb spring-data spring-data-mongodb


    【解决方案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 {...}
      

      我怀疑你可以这样设置任何注释属性。

      【讨论】:

        猜你喜欢
        • 2016-05-30
        • 1970-01-01
        • 2017-06-01
        • 2021-07-29
        • 2015-06-18
        • 2012-05-06
        • 2011-10-10
        • 1970-01-01
        • 2020-05-11
        相关资源
        最近更新 更多