Подтвердить что ты не робот

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

Я использую последний spring -data-mongodb (1.1.0.M2) и последний Mongo Driver (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.

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

Спасибо.

4b9b3361

Ответ 1

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

Тем не менее, все не было потеряно. Наш первоначальный план состоял в том, чтобы каждый клиент работал на своем собственном сервере приложений, указывая на свою собственную базу данных MongoDB на сервере MongoDB. Затем мы можем предоставить системную переменную -Dprovider= , и каждый сервер работает, указывая только на одну базу данных.

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

Я считаю, что есть способ, но для того, чтобы все это работало, это просто больше, чем описано в других сообщениях. Вы должны сделать свой собственный RepositoryFactoryBean. Вот пример из Spring Data MongoDB Reference Docs. Вам все равно придется реализовать свой собственный MongoTemplate и задержать или удалить вызов ensureIndexes(). Но вам придется переписать несколько классов, чтобы убедиться, что ваш MongoTemplate вызывается вместо Spring's. Другими словами, много работы. Работа, которую я хотел бы видеть, случается или даже делает, у меня просто не было времени.

Спасибо за ответы.

Ответ 2

Вот ссылка на статью, я думаю, это то, что вы ищете 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 - это коллекция! Image.java будет сохранен в коллекции изображений в базе данных imagedb.

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

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;
}

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

Ответ 3

Вы можете захотеть подклассом SimpleMongoDbFactory и определить, как возвращается DB по умолчанию, возвращаемая getDb. Один из вариантов - использовать локальные переменные потока, чтобы решить, использовать Db, вместо использования нескольких 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, а затем reset с помощью: ThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread() после того, как вы закончите.

Ответ 4

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

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

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

Ответ 5

Я использовал разные БД с помощью 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());    
}

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

Ответ 6

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

Я связал проект, который реализует многоуровневую аренду простым способом.

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

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

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

Литература:

multi-tenant-spring-mongodb