如何在Spring Data中为类配置MongoDb集合名称

33
我在MongoDB数据库中有一个名为Products的集合,它在我的Java代码中由接口IProductPrice表示。下面的存储库声明会导致Spring Date查找db.collection: Intelliprice.iProductPrice集合。
我希望使用外部配置将其配置为查找db.collection: Intelliprice.Products而不是在IProductPrice上放置@Collection(..)注释。这是否可行?我该如何做到这一点?
public interface ProductsRepository extends
    MongoRepository<IProductPrice, String> {
}
5个回答

27

您目前唯一可以实现这个的方法是,在领域类上使用@Document进行注释,并使用collection属性定义此类实例将被持久化到的集合名称。

然而,有一个JIRA问题正在处理中,建议添加一个可插拔的命名策略来全局配置处理类、集合和属性名称的方式。欢迎评论您的用例并投票支持。


3
谢谢,我知道@Document注解,并且可能最终会使用它。我基本上想要将配置从实际类中外部化。您链接的JIRA问题正在讨论命名策略,并仍建议使用注解进行自定义名称。 - Danish
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Oliver Drotbohm
1
如果您从父接口扩展文档,则此方法无效。如果该接口在存储库签名ReactiveMongoRepository<iProductPrice,String>中声明,则即使硬编码的集合名称为@Document(collection = "specific_collection_name"),也会被忽略。集合将是iproductprice - Zon
@Zon 如果是这种情况,您需要使用sepl方法在基础/父类级别设置集合名称。这个解决方案很容易实现。 - CᴴᴀZ
我已经尝试过使用SPEL,但这样会让所有后代都有一个集合名称。如果你调用某个方法或向原型Bean传递参数 - 无论何时集合名称只在创建Bean时设置一次。我甚至尝试了动态设置注解值 - 这也没有帮助。唯一剩下的选择就是重写Spring Data存储库到允许在查询中传递集合名称的MongoTemplate。 - Zon

21

参考Oliver Gierke的答案,在一个需要为一个实体创建多个集合的项目中,我想使用Spring repositories并在使用Repository之前需要指定使用的实体。

我成功地使用这个系统根据需要修改了Repository集合名称,它使用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());
}
否则,如果您需要使用配置变量,您可以尝试使用类似于这样的东西吗?来源
@Value("#{systemProperties['pop3.port'] ?: 25}") 

1
我还没有测试过这个代码,而且它也不是很干净,但是因为创意值得肯定,所以给你点赞加一 :) - alex
1
看起来你正在将“上下文”信息保存在一个可能被自动装配到不同位置的存储库中。我猜这个解决方案不是线程安全的。 - thanosa75
1
这会导致循环依赖错误,当我从Person bean中删除#{notificationRepository.getCollectionName()}时,它得到了解决。 - Rohitesh
你如何在测试类中创建一个新的集合?@Jeremie - Erdem Aydemir
1
找到了我的问题,原来是一个项目问题,因为我正在创建一个基于全新的MongoMappingContext的自定义MongoTemplate,而不是使用提供的MongoMappingContext。 - Constantino Cronemberger
显示剩余9条评论

10

可能有点晚了,但我发现你可以在spring-boot中直接访问应用程序配置,动态设置mongo的集合名称。

@Document(collection = "#{@environment.getProperty('configuration.property.key')}")
public class DomainModel {...}

我猜你可以通过这种方式设置任何注释属性。

3
我能提供的唯一建议是,您需要在bean名称前面添加@前缀:
collection = "#{@beanName.method()}"

对于bean工厂来注入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;
}

1
我也只能使用“@” bean 前缀来使其工作。不确定这种语法是否正确,因为在 @Oliver Drotbohm 提到的 Jira 问题 中也没有建议使用该语法,而解决方案也有其他文档记录。 - FrVaBe
使用“@”前缀引用Bean的用法在此处有文档记录:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions-bean-references。感谢Mark提供的提示:https://twitter.com/mp911de/status/1243151041568354306。 - FrVaBe
由于某种原因,这对我有效,但仅当bean名称的第一个字母是小写时才有效。因此,不要使用#{@ActualBeanName.method()}, 而应该使用#{@actualBeanName.method()} - Raphael

2

我在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();

希望它能帮到您。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接