Spring Boot中MongoTemplate多数据库实体读写隔离的实现方案咨询
Spring Boot中MongoTemplate多数据库实体读写隔离的实现方案咨询
我现在有两个MongoDB数据库:
- test:包含
people集合,对应Person和Author实体类 - test2:包含
somethingElse集合,对应SomethingElse实体类
目前遇到的问题是:自动注入的MongoTemplate只能操作test库,保存SomethingElse实体时,会在test库自动创建集合并插入数据。我想确保SomethingElse类的文档只在test2库中进行读写和映射,该怎么实现呢?
以下是我当前的代码实现:
MongoConfig.java
package com.example.demo; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import lombok.Getter; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; @SpringBootApplication public class MongoConfig extends AbstractMongoClientConfiguration { @Getter MongoClient client; @Override protected String getDatabaseName() { return "test"; } @Override public MongoClient mongoClient() { MongoClient client = MongoClients.create("mongodb://127.0.0.1:27017/test"); this.client = client; return client; } }
TestData.java
package com.example.demo; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import org.bson.Document; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.UUID; @Component public class TestData { @Autowired MongoConfig config; private static String FIRST_DB = "test"; private static String SECOND_DB = "test2"; private void insertFirstDatabase(){ MongoDatabase database = config.getClient().getDatabase(FIRST_DB); MongoCollection collection = database.getCollection("people"); Document person = new Document().append("_id",UUID.randomUUID().toString()) .append("name","Larry").append("email","larry@gmail.com").append("_class","person"); Document author = new Document().append("_id",UUID.randomUUID().toString()) .append("name","Frank").append("email","frank@gmail.com").append("articleCount",1).append("active",true) .append("_class","author"); collection.insertOne(person); collection.insertOne(author); } private void insertSecondDatabase(){ Document somethingElseDocument = new Document().append("_id",UUID.randomUUID().toString()) .append("name","example document").append("_class","somethingElse"); config.getClient().getDatabase(SECOND_DB).getCollection("somethingElse").insertOne(somethingElseDocument); } public void insert(){ insertFirstDatabase(); insertSecondDatabase(); } public void cleanUp() { MongoDatabase firstDatabase = config.getClient().getDatabase(FIRST_DB); firstDatabase.drop(); MongoDatabase secondDatabase = config.getClient().getDatabase(SECOND_DB); secondDatabase.drop(); } }
Test.java
package com.example.demo; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Component; import java.util.List; import java.util.UUID; @Component public class Test { @Autowired TestData testData; @Autowired MongoTemplate mongoTemplate; @PostConstruct public void startTest(){ testData.insert(); List<Person> allPeople = mongoTemplate.findAll(Person.class); for (Person person : allPeople) { System.out.println(person.toString()); } List<SomethingElse> otherEntities = mongoTemplate.findAll(SomethingElse.class); for (SomethingElse entity : otherEntities) { System.out.println(entity.toString()); } SomethingElse newEntity = new SomethingElse(UUID.randomUUID().toString(),"wrong database"); mongoTemplate.insert(newEntity); //Breakpoint here to view DB testData.cleanUp(); } }
Person.java
package com.example.demo; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.mongodb.core.mapping.Document; @Document(collection = "people") @TypeAlias("person") @AllArgsConstructor @NoArgsConstructor @Getter @Setter public class Person { @Id private String id; private String name; private String email; @Override public String toString(){ return this.getName() + " (PERSON)"; } }
Author.java
package com.example.demo; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.annotation.TypeAlias; @TypeAlias("author") @AllArgsConstructor @NoArgsConstructor @Getter @Setter class Author extends Person{ private Integer articleCount; private Boolean active; @PersistenceCreator public Author(String id, String name, String email, int articleCount, boolean active) { super(id,name,email); this.articleCount = articleCount; this.active = active; } @Override public String toString(){ return this.getName() + " (AUTHOR)"; } }
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>4.0.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>MongoDb Multiple DBs</name> <description>Demo project for Spring Boot</description> <url/> <licenses> <license/> </licenses> <developers> <developer/> </developers> <scm> <connection/> <developerConnection/> <tag/> <url/> </scm> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Source: https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-mongodb --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> <version>4.1.0-M2</version> <scope>compile</scope> </dependency> <!-- Source: https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.42</version> <scope>compile</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
针对问题的解决方案
方案一:配置多个独立的MongoTemplate实例
这是最清晰可控的方式,为每个数据库创建专属的MongoTemplate,使用时明确指定:
- 重构MongoConfig配置类
移除继承的AbstractMongoClientConfiguration,直接创建两个MongoTemplateBean:
package com.example.demo; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.mongodb.core.MongoTemplate; @SpringBootApplication public class MongoConfig { // 创建通用的MongoClient(连接到MongoDB服务,不指定默认数据库) private MongoClient mongoClient() { return MongoClients.create("mongodb://127.0.0.1:27017/"); } // 对应test数据库的MongoTemplate @Bean(name = "testMongoTemplate") public MongoTemplate testMongoTemplate() { return new MongoTemplate(mongoClient(), "test"); } // 对应test2数据库的MongoTemplate @Bean(name = "test2MongoTemplate") public MongoTemplate test2MongoTemplate() { return new MongoTemplate(mongoClient(), "test2"); } }
- 在业务类中指定使用对应的MongoTemplate
修改Test.java中的注入逻辑,通过@Qualifier标记选择对应的实例:
@Component public class Test { @Autowired TestData testData; // 操作test库的MongoTemplate @Autowired @Qualifier("testMongoTemplate") MongoTemplate testMongoTemplate; // 操作test2库的MongoTemplate @Autowired @Qualifier("test2MongoTemplate") MongoTemplate test2MongoTemplate; @PostConstruct public void startTest(){ testData.insert(); // 从test库读取Person/Author List<Person> allPeople = testMongoTemplate.findAll(Person.class); for (Person person : allPeople) { System.out.println(person.toString()); } // 从test2库读取SomethingElse List<SomethingElse> otherEntities = test2MongoTemplate.findAll(SomethingElse.class); for (SomethingElse entity : otherEntities) { System.out.println(entity.toString()); } // 插入到test2库 SomethingElse newEntity = new SomethingElse(UUID.randomUUID().toString(),"correct database"); test2MongoTemplate.insert(newEntity); testData.cleanUp(); } }
方案二:通过实体类注解直接指定数据库
如果你的业务场景相对简单,也可以直接在SomethingElse实体类上通过@Document的database属性绑定目标数据库:
修改SomethingElse.java:
package com.example.demo; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.mongodb.core.mapping.Document; @Document(collection = "somethingElse", database = "test2") @TypeAlias("somethingElse") @AllArgsConstructor @NoArgsConstructor @Getter @Setter public class SomethingElse { @Id private String id; private String name; @Override public String toString(){ return this.getName() + " (SomethingElse)"; } }
这种方式不需要修改MongoTemplate配置,但需要注意:默认的MongoTemplate需要拥有访问test2数据库的权限,且如果后续有更多跨库操作,多个MongoTemplate的方式会更易于维护。
额外注意事项
- 确保MongoDB的访问用户拥有
test和test2两个数据库的读写权限 - 原配置中
mongoClient()方法指定了默认数据库test,重构后改为连接到MongoDB服务本身,更灵活 - 如果使用Spring Data MongoDB的Repository接口,也可以为不同数据库配置对应的Repository工厂,实现更彻底的隔离
备注:内容来源于stack exchange,提问作者pomipiwimo
相关产品推荐
相关产品推荐

