Spring-data-mongodb подключается к нескольким базам данных в одном экземпляре Mongo

я использую последнюю версию spring-data-mongodb (1.1.0.M2) и последний драйвер Mongo (2.9.0-RC1). У меня есть ситуация, когда у меня есть несколько клиентов, подключающихся к моему приложению, и я хочу дать каждому из них свою собственную "схему/базу данных" на одном сервере Mongo. Это не очень сложная задача для достижения, если я использовал драйвер напрямую:

Mongo mongo = new Mongo( new DBAddress( "localhost", 127017 ) );

DB client1DB = mongo.getDB( "client1" );
DBCollection client1TTestCollection = client1DB.getCollection( "test" );
long client1TestCollectionCount = client1TTestCollection.count();

DB client2DB = mongo.getDB( "client2" );
DBCollection client2TTestCollection = client2DB.getCollection( "test" );
long client2TestCollectionCount = client2TTestCollection.count();

см., легкий. Но spring-data-mongodb не позволяет легко использовать несколько баз данных. Предпочтительный способ настройки подключение к Mongo расширения AbstractMongoConfiguration класс:

вы увидите, что вы переопределить следующий метод:

getDatabaseName()

так это заставляет вас использовать одно имя базы данных. Интерфейсы репозитория, которые вы затем создаете, используют это имя базы данных внутри MongoTemplate, которое передается в SimpleMongoRepository класса.

где на земле я бы вставил несколько имен базы данных? Я должен сделать несколько имен базы данных, несколько MongoTempates (по одному на имя базы данных) и несколько других классов конфигурации. И это все еще не заставляет мои интерфейсы репозитория использовать правильный шаблон. Если кто-нибудь пробовал такое, дайте мне знать. Если я это выясню, я опубликую ответ здесь.

спасибо.

6 ответов


вот ссылка на статью, я думаю, это то, что вы ищете http://michaelbarnesjr.wordpress.com/2012/01/19/spring-data-mongo/

ключ должен предоставить несколько шаблонов

настройки шаблона для каждой базы данных.

<bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg ref="mongoConnection"/>
    <constructor-arg name="databaseName" value="vehicledatabase"/>
</bean>

настройки шаблона для каждой базы данных.

<bean id="imageTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
        <constructor-arg ref="mongoConnection"/>
        <constructor-arg name="databaseName" value="imagedatabase"/>
</bean>

<bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
    <constructor-arg ref="mongoConnection"/>
    <constructor-arg name="databaseName" value="vehicledatabase"/>
</bean>

теперь вам нужно сказать Spring, где находятся ваши репозитории, чтобы он мог их вводить. Все они должны быть в одном каталоге. Я пытался имейте их в разных подкаталогах, и это не сработало правильно. Так они все в каталог.

<mongo:repositories base-package="my.package.repository">
    <mongo:repository id="imageRepository" mongo-template-ref="imageTemplate"/>
    <mongo:repository id="carRepository" mongo-template-ref="vehicleTemplate"/>
    <mongo:repository id="truckRepository" mongo-template-ref="vehicleTemplate"/>
</mongo:repositories>

каждый репозиторий является интерфейсом и записывается следующим образом (Да, вы можете оставить их пустыми):

@Repository
public interface ImageRepository extends MongoRepository<Image, String> {

}

@Repository
public interface TruckRepository extends MongoRepository<Truck, String> {

}

имя частной переменной imageRepository - это коллекция! изображения.java будет сохранен в коллекцию изображений в базе данных imagedb.

здесь вы можете найти, вставить и удалить отчеты:

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;
}

С помощью Autowiring вы сопоставляете имя переменной с именем (id) в вашей конфигурации.


вы можете захотеть подкласс SimpleMongoDbFactory и выработать стратегию, как БД по умолчанию, как возвращается getDb возвращается. Один из вариантов-использовать локальные переменные потока для выбора используемой БД вместо использования нескольких MongoTemplates.

что-то вроде этого:

public class ThreadLocalDbNameMongoDbFactory extends SimpleMongoDbFactory {
    private static final ThreadLocal<String> dbName = new ThreadLocal<String>();
    private final String defaultName; // init in c'tor before calling super

    // omitted constructor for clarity

    public static void setDefaultNameForCurrentThread(String tlName) {
        dbName.set(tlName);
    }
    public static void clearDefaultNameForCurrentThread() {
        dbName.remove();
    }

    public DB getDb() {
        String tlName = dbName.get();
        return super.getDb(tlName != null ? tlName : defaultName);
    }
}

затем переопределите mongoDBFactory() в своем @Configuration класс, который простирается от AbstractMongoConfiguration вот так:

@Bean
@Override
public MongoDbFactory mongoDbFactory() throws Exception {
  if (getUserCredentials() == null) {
      return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName());
  } else {
      return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials());
  }
}

в вашем клиентском коде (возможно, ServletFilter или какой-то такой) вам нужно будет вызов: ThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread() перед выполнением любой работы Mongo и впоследствии сбросьте ее с помощью: ThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread() после того как вы сделали.


Итак, после долгих исследований и экспериментов, я пришел к выводу, что это не возможно с текущей . Я попробовал метод Байи выше и столкнулся с определенным препятствием. The MongoTemplate работает ensureIndexes() метод из своего конструктора. Этот метод вызывает базу данных, чтобы убедиться, что аннотированные индексы существуют в базе данных. Конструктор для MongoTemplate вызывается, когда Spring запускается, поэтому у меня даже нет возможности установить ThreadLocal переменной. Мне надо по умолчанию уже установлено, когда Spring начинается, затем измените его, когда придет запрос. Это недопустимо, потому что я не хочу и у меня нет базы данных по умолчанию.

однако не все было потеряно. Наш первоначальный план состоял в том, чтобы каждый клиент работал на своем собственном сервере приложений, указывая на свой собственный


место, чтобы посмотреть на это MongoDbFactory интерфейс. Основная реализация этого принимает экземпляр Mongo и работает с ним на протяжении всего срока службы приложения. Чтобы добиться использования базы данных для каждого потока (и, следовательно, для каждого запроса), вам, вероятно, придется реализовать что-то вроде AbstractRoutingDataSource. Идея заключается в том, что у вас есть метод шаблона, который должен будет искать арендатора для вызова (ThreadLocal Баунд, я думаю), а затем выберите Mongo экземпляр из набора стандартных или пользовательскую логику, чтобы придумать новый для нового арендатора и т. д.

имейте в виду, что MongoDbFactory обычно get используется через getDb() метод. Тем не менее, в MongoDB есть функции, которые нам нужно предоставить getDb(String name). DBRefs (sth. как внешний ключ в реляционном мире) может указывать на документы совершенно другой базы данных. Поэтому, если вы делаете делегирование, либо избегайте использования этой функции (я думаю, что DBRefs указание на другую БД-единственные места, вызывающие getDb(name)) или явно справится.

С точки зрения конфигурации вы можете либо просто переопределить mongoDbFactory() полностью или просто не расширять базовый класс вообще и придумать свою собственную конфигурацию на основе Java.


я использовал другую БД, используя Java Config, вот как я это сделал:

@Bean 
public MongoDbFactory mongoRestDbFactory() throws Exception { 
    MongoClientURI uri=new MongoClientURI(environment.getProperty("mongo.uri")); 
    return new SimpleMongoDbFactory(uri);
}

@Override
public String getDatabaseName() {
    return "rest";
}

@Override
public @Bean(name = "secondaryMongoTemplate") MongoTemplate mongoTemplate() throws Exception{ //hay que cambiar el nombre de los templates para que el contendor de beans sepa la diferencia  
    return new MongoTemplate(mongoRestDbFactory());    
}

а другой был такой:

@Bean 
public MongoDbFactory restDbFactory() throws Exception {
    MongoClientURI uri = new MongoClientURI(environment.getProperty("mongo.urirestaurants")); 
    return new SimpleMongoDbFactory(uri);
}

@Override
public String getDatabaseName() {
    return "rest";
}

@Override
public @Bean(name = "primaryMongoTemplate") MongoTemplate mongoTemplate() throws Exception{ 
    return new MongoTemplate(restDbFactory());    
}

поэтому, когда мне нужно изменить мою базу данных, я только выбираю, какую конфигурацию использовать


насколько я понимаю, вы хотите больше гибкости в изменении текущей БД на лету.

Я связал проект, который реализует multi tenancy простым способом.

Он может быть использован в качестве отправной точки для приложения.

он реализует SimpleMongoDbFactory и предоставляет пользовательский метод getDB для разрешения правильной БД для использования в определенный момент. Его можно улучшить многими способами, например, путем извлечения сведений о БД из HttpSession из Объект SpringSession, который, например, может быть кэширован Redis .

чтобы иметь разные mongoTemplates, используя разные dbs одновременно, возможно, измените область вашего mongoDbFactory на сеанс.

ссылки:

multi-tenant-весна-mongodb