“目标:实现不同分组的用户访问自己的专用数据库 (数据库在同一个服务器). 例:A 公司用户,仅可访问 A 公司的专用数据库
”
要求:MongoDB + SpringBoot
Pom 依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</dependency>
<!--添加REST依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
</dependencies>
1. 自定义 MongoDBFactoty
/**
* 类的描述:
* <p>自定义MongoDB Factory </p>
* 自定义MongoDb 配置工厂,可实现多租户切换访问。
*
* @author jei0439
* @date 2018/9/14 16:30
*/
public class MultiTenantMongoDbFactory extends SimpleMongoDbFactory {
private static final Logger logger = LoggerFactory.getLogger(MultiTenantMongoDbFactory.class);
/**
* 默认数据库名称
**/
private final String defaultName;
/**
* MongoDB模板类
**/
private MongoTemplate mongoTemplate;
/**
* 用户所在线程使用数据库集合
**/
private static final ThreadLocal<String> dbName = new ThreadLocal<String>();
/**
*
**/
private static final HashMap<String, Object> databaseIndexMap = new HashMap<String, Object>();
public MultiTenantMongoDbFactory(final MongoClient mongo, final String defaultDatabaseName) {
super(mongo, defaultDatabaseName);
logger.debug("Instantiating " + MultiTenantMongoDbFactory.class.getName() + " with default database name: " + defaultDatabaseName);
this.defaultName = defaultDatabaseName;
}
/**
*
* 功能描述: dirty but ... what can I do?
*
* @param
* @author jei0439
* @date 2018/9/17 10:55
*/
public void setMongoTemplate(final MongoTemplate mongoTemplate) {
Assert.isNull(this.mongoTemplate, "You can set MongoTemplate just once");
this.mongoTemplate = mongoTemplate;
}
/**
*
* 功能描述: 将databaseName放入dbName
*
* @param databaseName database/scheme名称
* @author jei0439
* @date 2018/9/17 10:56
*/
public static void setDatabaseNameForCurrentThread(final String databaseName) {
logger.debug("Switching to database: " + databaseName);
dbName.set(databaseName);
}
/**
*
* 功能描述: 清空dbName
*
* @author jei0439
* @date 2018/9/17 10:57
*/
public static void clearDatabaseNameForCurrentThread() {
if (logger.isDebugEnabled()) {
logger.debug("Removing database [" + dbName.get() + "]");
}
dbName.remove();
}
@Override
public MongoDatabase getDb() {
final String tlName = dbName.get();
final String dbToUse = (tlName != null ? tlName : this.defaultName);
logger.debug("Acquiring database: " + dbToUse);
createIndexIfNecessaryFor(dbToUse);
return super.getDb(dbToUse);
}
/**
*
* 功能描述: TODO: 没搞明白作用
*
* @param database database/scheme 名称
* @author jei0439
* @date 2018/9/17 10:58
*/
private void createIndexIfNecessaryFor(final String database) {
if (this.mongoTemplate == null) {
logger.error("MongoTemplate is null, will not create any index.");
return;
}
// sync and init once
boolean needsToBeCreated = false;
synchronized (MultiTenantMongoDbFactory.class) {
final Object syncObj = databaseIndexMap.get(database);
if (syncObj == null) {
databaseIndexMap.put(database, new Object());
needsToBeCreated = true;
}
}
// make sure only one thread enters with needsToBeCreated = true
synchronized (databaseIndexMap.get(database)) {
if (needsToBeCreated) {
logger.debug("Creating indices for database name=[" + database + "]");
createIndexes();
logger.debug("Done with creating indices for database name=[" + database + "]");
}
}
}
private void createIndexes() {
final MongoMappingContext mappingContext = (MongoMappingContext) this.mongoTemplate.getConverter().getMappingContext();
final MongoPersistentEntityIndexResolver indexResolver = new MongoPersistentEntityIndexResolver(mappingContext);
for (BasicMongoPersistentEntity<?> persistentEntity : mappingContext.getPersistentEntities()) {
checkForAndCreateIndexes(indexResolver, persistentEntity);
}
}
private void checkForAndCreateIndexes(final MongoPersistentEntityIndexResolver indexResolver, final MongoPersistentEntity<?> entity) {
// make sure its a root document
if (entity.findAnnotation(Document.class) != null) {
for (IndexDefinitionHolder indexDefinitionHolder : indexResolver.resolveIndexFor(entity.getTypeInformation())) {
// work because of javas reentered lock feature
this.mongoTemplate.indexOps(entity.getType()).ensureIndex(indexDefinitionHolder);
}
}
}
}
2.MongoConfig
/**
* 类的描述:
* <p>自定义MongoDB Config </p>
* Spring 此处文件配置初始化 MongoDB 配置、自定义MongoDB Factory
*
* @author jei0439
* @date 2018/9/14 16:30
*/
@Configuration
@EnableAutoConfiguration
public class MongoConfig {
private final MongoClientOptions options;
private final MongoClientFactory factory;
private MongoClient mongo;
/**
*
* 功能描述: 构造函数 直接使用MongoAutoConfiguration构造函数 参数初始化由Spring完成
*
* @param properties MongoDB 配置数据
* @param options TODO: 待完善 断点跟踪Spring框架传参进来是null
* @param environment TODO: 待完善 断点跟踪Spring框架传参进来是null
* @author jei0439
* @date 2018/9/17 10:48
*/
public MongoConfig(MongoProperties properties, ObjectProvider<MongoClientOptions> options, Environment environment) {
this.options = (MongoClientOptions)options.getIfAvailable();
this.factory = new MongoClientFactory(properties, environment);
}
/**
*
* 功能描述: 覆盖默认的MongoDbFactory
*
* @author jei0439
* @date 2018/9/17 10:51
*/
@Bean
MultiTenantMongoDbFactory mongoDbFactory() {
this.mongo = this.factory.createMongoClient(this.options);
MultiTenantMongoDbFactory mongoDbFactory = new MultiTenantMongoDbFactory(mongo,"pvQC_dev");
return mongoDbFactory;
}
/**
*
* 功能描述: Create MongoTemplate
*
* @author jei0439
* @date 2018/9/17 10:51
*/
@Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
}
3. 测试 Entity
/**
* 类的描述:
* <p></p>
*
* @author jei0439
* @date 2018/9/17 10:30
*/
public class User {
@Id
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4.Repositoty
/**
* 类的描述:
* <p></p>
*
* @author jei0439
* @date 2018/9/17 10:31
*/
@Component
public interface Repository extends MongoRepository<User,Long> {
User getById(Long id);
User getByName(String name);
}
5.Application (随便测试)
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
Repository repository;
@Override
public void run(final String... args) throws Exception {
// add something to the default database (test)
User user = new User();
user.setName("张三");
user.setId(1L);
// this.repository.save(user);
System.out.println("data from test: " + this.repository.findAll());
// okay? fine. - lets switch the database
MultiTenantMongoDbFactory.setDatabaseNameForCurrentThread("test666");
this.repository.save(user);
// should be empty
System.out.println("data from test666: " + this.repository.findAll());
// switch back and clean up
MultiTenantMongoDbFactory.setDatabaseNameForCurrentThread("test");
this.repository.save(user);
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
运行测试结果
作者:jei0439
来源链接:
https://blog.csdn.net/jei0439/article/details/82735055