java - query - Spring-data-mongodb se conecta a mĂșltiples bases de datos en una instancia de Mongo
spring data mongodb query (6)
Aquí hay un enlace a un artículo que creo que es lo que está buscando http://michaelbarnesjr.wordpress.com/2012/01/19/spring-data-mongo/
La clave es proporcionar múltiples plantillas.
configurar una plantilla para cada base de datos.
<bean id="vehicleTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoConnection"/>
<constructor-arg name="databaseName" value="vehicledatabase"/>
</bean>
configurar una plantilla para cada base de datos.
<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>
Ahora, debe decirle a Spring dónde están sus repositorios para que pueda inyectarlos. Todos ellos deben estar en el mismo directorio. Traté de tenerlos en diferentes subdirectorios, y no funcionó correctamente. Así que todos están en el directorio del repositorio.
<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>
Cada repositorio es una interfaz y se escribe de la siguiente manera (sí, puede dejarlos en blanco):
@Repository
public interface ImageRepository extends MongoRepository<Image, String> {
}
@Repository
public interface TruckRepository extends MongoRepository<Truck, String> {
}
El nombre de la variable privada imageRepository
es la colección! Image.java se guardará en la colección de imágenes dentro de la base de datos imagedb.
Aquí es cómo puede encontrar , insertar y eliminar registros:
@Service
public class ImageService {
@Autowired
private ImageRepository imageRepository;
}
Al autowiring usted hace coincidir el nombre de la variable con el nombre (id) en su configuración.
Estoy usando el último mongodb de datos de primavera (1.1.0.M2) y el último controlador Mongo (2.9.0-RC1). Tengo una situación en la que tengo varios clientes conectados a mi aplicación y quiero dar a cada uno su propio "esquema / base de datos" en el mismo servidor Mongo. Esta no es una tarea muy difícil de lograr si estaba usando el controlador directamente:
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();
Mira, fácil. Pero spring-data-mongodb no permite una forma fácil de usar múltiples bases de datos. La forma preferida de configurar una conexión a Mongo
es extender la clase AbstractMongoConfiguration :
Verás que anulas el siguiente método:
getDatabaseName()
Así que te obliga a usar un nombre de base de datos. Las interfaces de repositorio que construye utilizan ese nombre de base de datos dentro de MongoTemplate que se pasa a la clase SimpleMongoRepository
.
¿Dónde demonios pegaría múltiples nombres de bases de datos? Tengo que hacer varios nombres de bases de datos, varios MongoTempate
s (uno por nombre de base de datos) y varias otras clases de configuración. Y eso todavía no logra que las interfaces de mi repositorio utilicen la plantilla correcta. Si alguien ha intentado tal cosa, hágamelo saber. Si lo descubro, publicaré la respuesta aquí.
Gracias.
Así que después de mucha investigación y experimentación, he llegado a la conclusión de que esto todavía no es posible con el actual proyecto spring-data-mongodb
. Probé el método de Baja arriba y me encontré con un obstáculo específico. El MongoTemplate
ejecuta su método ensureIndexes()
desde su constructor. Este método llama a la base de datos para asegurarse de que existen índices anotados en la base de datos. Se llama al constructor de MongoTemplate
cuando se inicia Spring
por lo que nunca tengo la oportunidad de establecer una variable ThreadLocal
. Tengo que tener un valor predeterminado ya establecido cuando se inicia Spring
, luego cambiarlo cuando entra una solicitud. Esto no está permitido porque no quiero ni tengo una base de datos predeterminada.
Aunque no todo estaba perdido. Nuestro plan original era que cada cliente se ejecutara en su propio servidor de aplicaciones, apuntado a su propia MongoDB
datos MongoDB
en el servidor MongoDB
. Luego podemos proporcionar una variable del sistema -Dprovider=
y cada servidor ejecuta apuntando solo a una base de datos.
Se nos indicó que ThreadLocal
una aplicación multiusuario, de ahí el intento de la variable ThreadLocal
. Pero como no funcionó, pudimos ejecutar la aplicación tal como lo habíamos diseñado originalmente.
Creo que hay una manera de hacer que todo esto funcione, solo se necesita más de lo que se describe en las otras publicaciones. Tienes que hacer tu propio RepositoryFactoryBean
. Este es el ejemplo de los documentos de referencia de Spring Data MongoDB . Aún tendría que implementar su propia MongoTemplate
y demorar o eliminar la llamada a ensureIndexes()
. Pero tendría que volver a escribir algunas clases para asegurarse de que se MongoTemplate
su MongoTemplate
lugar de Spring''s
. En otras palabras, mucho trabajo. El trabajo que me gustaría ver suceder o incluso hacer, simplemente no tenía tiempo.
Gracias por las respuestas.
El lugar para mirar es la interfaz de MongoDbFactory
. La implementación básica de eso toma una instancia de Mongo y funciona con eso durante toda la vida útil de la aplicación. Para lograr un uso de base de datos por subproceso (y, por tanto, por solicitud), probablemente deba implementar algo en la línea de AbstractRoutingDataSource . La idea es que tienes un método de plantilla que tendrá que buscar el arrendatario por invocación ( ThreadLocal
bound, supongo) y luego seleccionar una instancia de Mongo
de un conjunto de predefinidas o alguna lógica personalizada para crear una nueva. un nuevo inquilino etc.
Tenga en cuenta que MongoDbFactory
generalmente se usa a través del método getDb()
. Sin embargo, hay características en MongoDB que nos necesitan para proporcionar un getDb(String name)
. DBRef
s (algo así como una clave externa en el mundo relacional) puede apuntar a documentos de una base de datos completamente diferente. Entonces, si está haciendo la delegación, evite usar esa función (creo que los DBRef
apuntan a otra base de datos son los únicos lugares que llaman a getDb(name)
) o lo manejan explícitamente.
Desde el punto de vista de la configuración, puede simplemente anular mongoDbFactory()
completamente o simplemente no ampliar la clase base y crear su propia configuración basada en Java.
Es posible que desee subdividir SimpleMongoDbFactory
y planear cómo se devuelve la base de datos predeterminada devuelta por getDb
. Una opción es usar variables locales de subproceso para decidir qué Db usar, en lugar de usar múltiples MongoTemplates.
Algo como esto:
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);
}
}
Luego, mongoDBFactory()
en su clase @Configuration
que se extiende desde AbstractMongoConfiguration
manera:
@Bean
@Override
public MongoDbFactory mongoDbFactory() throws Exception {
if (getUserCredentials() == null) {
return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName());
} else {
return new ThreadLocalDbNameMongoDbFactory(mongo(), getDatabaseName(), getUserCredentials());
}
}
En su código de cliente (tal vez un ServletFilter o algo similar) deberá llamar a: ThreadLocalDBNameMongoRepository.setDefaultNameForCurrentThread()
antes de realizar cualquier trabajo de Mongo y luego restablecerlo con: ThreadLocalDBNameMongoRepository.clearDefaultNameForCurrentThread()
Por lo que entiendo, usted quiere más flexibilidad para cambiar la base de datos actual sobre la marcha.
He vinculado un proyecto que implementa la tenencia múltiple de una manera simple.
Podría ser utilizado como punto de partida para la aplicación.
Implementa SimpleMongoDbFactory y proporciona un método getDB personalizado para resolver la db correcta para usar en un momento determinado. Se puede mejorar de muchas maneras, por ejemplo, recuperando los detalles de la base de datos de un objeto HttpSession from SpringSession, que, por ejemplo, Redis podría almacenar en caché.
Para tener diferentes mongoTemplates usando diferentes dbs al mismo tiempo, tal vez cambie el alcance de su mongoDbFactory a sesión.
Referencias:
Usé diferentes DB usando java Config, así es como lo hice:
@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());
}
Y el otro fue así:
@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());
}
Entonces, cuando necesito cambiar mi base de datos, solo selecciono qué configuración usar