How To Configure MongoDb Collection Name For a Class in Spring Data

I have a collection called Products in my MongoDB database, which is represented by the interface IProductPrice in my Java code. The following repository declaration causes Spring Date to look to the collection db.collection: Intelliprice.iProductPrice.

I want it to configure it to look in db.collection: Intelliprice.Products using an external configuration rather than putting an @Collection(..) annotation on IProductPrice. Is this possible? How can I do this?

public interface ProductsRepository extends
    MongoRepository<IProductPrice, String> {
}

Solution 1:

The only way you can currently achieve this is by annotating your domain class with @Document using the collection property to define the name of the collection instances of this class shall be persisted to.

However, there's a JIRA issue open that suggests adding a pluggable naming strategy to configure the ways class, collection and property names are handled in a more global way. Feel free to comment your use case and vote it up.

Solution 2:

using answer from Oliver Gierke above, working on a project where I need to create multiple collections for one entity, I wanted to use the spring repositories and needed to specify the entity to use before using the repository.

I managed to modify the repository collection name on demand using this system, it using SPeL. You can only work on 1 collection at a time though.

Domain object

@Document(collection = "#{personRepository.getCollectionName()}")
public class Person{}

Default Spring Repository:

public interface PersonRepository 
     extends MongoRepository<Person, String>, PersonRepositoryCustom{
}

Custom Repository Interface:

public interface PersonRepositoryCustom {
    String getCollectionName();

    void setCollectionName(String collectionName);
}

implementation:

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;
    }
}

To use it:

@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());
}

Otherwise if you need to use configuration variables, you could maybe use something like this? source

@Value("#{systemProperties['pop3.port'] ?: 25}") 

Solution 3:

A little late, but I've found you can set the mongo collection name dynamically in spring-boot accessing the application configuration directly.

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

I suspect you can set any annotation attribute this way.

Solution 4:

The only comment I can add is that you have to add @ prefix to the bean name:

collection = "#{@beanName.method()}"

for the bean factory to inject the bean:

@Document(collection = "#{@configRepositoryCustom.getCollectionName()}")
public class Config {

}

I struggled to figure it out..

COMPLETE EXAMPLE:

@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);
}

usage in 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;
}

Solution 5:

I use static class and method in 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();
    }
}

In Entity class ,@Document(collection = "#{T(com.test.data.CollectionNameHolder).get()}")

And then ,use

CollectionNameHolder.set("testx_"+pageNum) 

in Service , and

CollectionNameHolder.reset();

Hope it helps you.