SpringBoot使用MongoTemplate动态切换MongoDB

本文介绍了如何在SpringBoot应用中使用AOP结合MongoTemplate实现在HTTP请求中通过header参数动态切换MongoDB数据库。由于AOP不保证线程安全,通过引入线程局部变量ThreadLocal解决了并发问题,创建MongoTemplate子类并重写getDB()方法,确保每个线程使用对应的数据库连接,从而在保证线程安全的同时避免性能损失。
摘要由CSDN通过智能技术生成

SpringBoot使用MongoTemplate动态切换Mongo


    当前有一个项目包含一套代码和1个mongo的数据库,但要给2个部门用户使用,而且要做数据隔离,现有做法是部署2套代码和2个mongo数据库,2个域名,这样就能各自访问各自的系统,做到隔离。但为节省资源和减少运维工作,计划将2个系统合并为一个系统,一套代码部署,2个数据库。那么如何使用最少的成本完成系统升级呢。

    计划的方案,2个数据库,一个为A,一个为B。在http添加一个header参数来区分需要访问的是A还是B。在代码中获取header参数来切换数据库。

    代码是springboot架构。mongo部分代码是Repository->MongoTemplate,MongoTempla做CURD操作,所以准备使用AOP的方式,在调用MongoTemplate做CURD时切换数据库。就是实例化连接2个数据库的MongoTemplate,然后通过AOP在Repository调用时,根据不同的数据库重新赋值MongoTemplate。在网上搜索了下,基本也是这个思路。看来这个方案是可行的。但实际开发中有一个问题,AOP不是线程安全的,而MongoTemplate是单例,这样并发时MongoTemplate会被错误的赋值。所以还有一个急需解决的问题就是线程安全。首先想的办法是同步锁,在AOP时,使用synchronized将赋值和jionpoitn.proceed()同步,这样解决了线程安全的问题。但这样访问都变成的单线程模式,性能大幅下降,只好改变思路,给MongoTemplate赋值的数据库连接保存到线程变量里面,MongTemplate在进行数据库操作时使用当前线程的连接,查看了下MongoTemplate的源码,看到一个getDB(),每次find时通过getDB()获取的数据库连接,于是产生一个思路,写一个MongTemplate的子类,定义一个ThreadLoacl,AOP时将对应数据库的MongoFactory保存到ThreadLocal中,重载getDB(),ThreadLocal.get().getDB(),然后将这个子重新类赋值到Repository中替换掉已有的MongoTemplate。这样保证线程安全,而且性能太大的损失。

切换的AOP代码

import com.mongodb.MongoClient;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import ucan.xdf.com.config.MongoDBConfigProperties
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在 Spring Boot 中整合 MongoDB 多数据源,需要按照以下步骤操作: 1. 引入 MongoDB 的依赖 在 pom.xml 文件中引入 MongoDB 的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> ``` 2. 配置数据源 在 application.properties 文件中配置多个数据源: ```properties # 数据源1 spring.data.mongodb.uri=mongodb://localhost:27017/db1 # 数据源2 mongodb2.uri=mongodb://localhost:27017/db2 ``` 3. 配置多数据源 创建多个数据源的配置类,继承自 `AbstractMongoConfiguration`,并重写 `mongoClient()` 方法: ```java @Configuration public class DataSourceConfig1 extends AbstractMongoConfiguration { @Value("${spring.data.mongodb.uri}") private String uri; @Override protected String getDatabaseName() { return "db1"; } @Override public MongoClient mongoClient() { return new MongoClient(new MongoClientURI(uri)); } } @Configuration public class DataSourceConfig2 extends AbstractMongoConfiguration { @Value("${mongodb2.uri}") private String uri; @Override protected String getDatabaseName() { return "db2"; } @Override public MongoClient mongoClient() { return new MongoClient(new MongoClientURI(uri)); } } ``` 4. 配置动态数据源 创建一个动态数据源,实现 `AbstractRoutingDataSource` 接口,重写 `determineCurrentLookupKey()` 方法: ```java public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } } ``` 5. 配置数据源上下文 创建一个数据源上下文,用于保存当前使用的数据源的名称: ```java public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setDataSource(String dataSource) { contextHolder.set(dataSource); } public static String getDataSource() { return contextHolder.get(); } public static void clearDataSource() { contextHolder.remove(); } } ``` 6. 配置事务管理器 创建一个事务管理器,用于管理多个数据源的事务: ```java @Configuration @EnableTransactionManagement public class TransactionConfig implements TransactionManagementConfigurer { @Autowired private DynamicDataSource dynamicDataSource; @Override public PlatformTransactionManager annotationDrivenTransactionManager() { return new DataSourceTransactionManager(dynamicDataSource); } } ``` 7. 完成动态数据源配置 在 `Application` 类中完成动态数据源的配置: ```java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public DynamicDataSource dynamicDataSource(DataSourceConfig1 dataSourceConfig1, DataSourceConfig2 dataSourceConfig2) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("db1", dataSourceConfig1.mongoClient()); targetDataSources.put("db2", dataSourceConfig2.mongoClient()); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setDefaultTargetDataSource(dataSourceConfig1.mongoClient()); dynamicDataSource.setTargetDataSources(targetDataSources); return dynamicDataSource; } @Bean public MongoClient mongoClient(DynamicDataSource dynamicDataSource) { return dynamicDataSource; } @Bean public MongoTemplate mongoTemplate(DynamicDataSource dynamicDataSource) { return new MongoTemplate(dynamicDataSource); } } ``` 至此,就完成了 Spring Boot 整合 MongoDB 多数据源动态切换的配置。在需要切换数据源的地方,可以使用 `DataSourceContextHolder.setDataSource("db1")` 进行动态切换
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值