Java实现动态增加和切换数据源以访问不同的数据库

        有时候我们需要把数据存放到多个数据库中,但是一个数据源只能访问一个数据库。想访问不同的数据库,那么就需要切换不同的数据源。有时候我们要切换的数据源是未知的,在程序运行的过程中才能知道要访问哪一个数据库,这时候就需要使用动态增加数据源的方法。我们可以先在配置文件中配置一个默认数据源,程序运行过程中需要访问其它数据库的时候,就动态的创建新的数据源织入到程序当中,让程序使用该新建的数据源。将这些新建的数据源缓存起来,后面需要用到就可以获取到,实现切换不同的数据源。主要的步骤为:“首先在配置文件中配置一个默认数据源”、“新建DynamicDataSource类来实现切换不同的数据源”、“DynamicDataSource实现动态增加数据源”、“使用Spring的AOP,将线程ThreadLocal的dbName变量清空,防止影响下一次访问数据库”、“在我们的产品中,不同的公司对应着不同的数据库dbName,所以可以根据dbName创建不同的数据源”。

1、    首先在配置文件中配置一个默认数据源。
在ApplicationContext.xml中配置数据源。
(1)    配置sqlSessionFactory,指定数据源为dynamicDataSource:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dynamicDataSource" />
		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
	</bean>

(2)    配置dynamicDataSource,为自定义的DynamicDataSource类,负责切换数据源和动态增加数据源,指定了默认数据源为jdbcDataSource_nbr_bx:

<!--动态数据源的配置 -->
	<bean id="dynamicDataSource" class="com.bx.erp.action.interceptor.DynamicDataSource" primary="true">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry value-ref="jdbcDataSource_nbr_bx" key="jdbcDataSource_nbr_bx" />
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="jdbcDataSource_nbr_bx" />
	</bean>

(3)    配置默认数据源jdbcDataSource_nbr_bx:

<bean id="jdbcDataSource_nbr_bx" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName">
			<value>${driverClassName}</value>
		</property>
		<property name="url">
			<value>jdbc:mysql://localhost:3306/nbr_bx?useUnicode=true&amp;serverTimezone=Asia/Shanghai&amp;characterEncoding=utf8&amp;zeroDateTimeBehavior=CONVERT_TO_NULL</value>
		</property>
		<property name="username">
			<value>${db.nbx.mysql.username}</value>
		</property>
		<property name="password">
			<value>${db.nbx.mysql.password}</value>
		</property>
	</bean>

2、    新建DynamicDataSource类来实现切换不同的数据源。
Spring框架有一个AbstractRoutingDataSource类,它可以在程序运行的时候,把某个数据源动态织入到程序中,以便访问不同的数据库。它有一个determineCurrentLookupKey方法,该方法返回一个键key,通过这个key,在数据源集合中获取其中一个数据源。最后根据获取到的数据源去访问数据库。所以DynamicDataSource类可以继承AbstractRoutingDataSource,实现determineCurrentLookupKey方法,通过返回不同的key实现切换不同的数据源,默认使用配置文件中的数据源。

public class DynamicDataSource extends AbstractRoutingDataSource {
	@Override
	protected Object determineCurrentLookupKey() {
		String dbName = DataSourceContextHolder.getDbName();

		if (dbName == null) {
			dbName = DataSourceContextHolder.DATASOURCE_jdbcDataSource_nbr_bx; // 默认使用公共DB:nbr_bx
		} else {
			this.selectDataSource(dbName);
			if (dbName.equals(DataSourceContextHolder.DATASOURCE_jdbcDataSource_nbr_bx)) {
				dbName = DataSourceContextHolder.DATASOURCE_jdbcDataSource_nbr_bx;
			}
		}
		logger.debug("--------> using datasource " + dbName);

		return dbName;
	}
……

3、    DynamicDataSource实现动态增加数据源。
我们在配置文件中只配置了一个默认数据源,那么需要增加数据源,才能够切换不同的数据源访问不同的数据库。
增加一个数据源的步骤如下:
例如我们要访问一个名称为nbr_test的数据库,这个访问操作对应着某一个线程,线程要访问哪一个数据库,我们可以使用ThreadLocal类来设置和获取。ThreadLocal类可以防止一个线程的变量被其它线程修改。
我们用DataSourceContextHolder封装ThreadLocal:

public class DataSourceContextHolder{
	public static final String DATASOURCE_jdbcDataSource_nbr_bx = "jdbcDataSource_nbr_bx";

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    
    public static void setDbName(String dbName) {
        contextHolder.set(dbName);
    }
    public static String getDbName() {
        return ((String) contextHolder.get());
    }
    public static void clearDbName() {
        contextHolder.remove();
    }
  
}

在访问数据库前,我们设置访问nbr_test这个数据库:

DataSourceContextHolder.setDbName(dbName);

DynamicDataSource的determineCurrentLookupKey方法获取到当前线程的数据库名称dbName:

String dbName = DataSourceContextHolder.getDbName();

DynamicDataSource的selectDataSource方法从 targetDataSources这个hashmap中查找是否存在这个数据源,因为现在还没有这个数据源,所以需要去创建,后面创建好了之后,也会把创建好的数据源存到这个targetDataSources中:

/** @describe 数据源存在时不做处理,不存在时创建新的数据源链接,并将新数据链接添加至缓存 */
	public void selectDataSource(String dbName) {
		String sid = DataSourceContextHolder.getDbName();
		if (DataSourceContextHolder.DATASOURCE_jdbcDataSource_nbr_bx.equals(dbName)) {
			DataSourceContextHolder.setDbName(DataSourceContextHolder.DATASOURCE_jdbcDataSource_nbr_bx);
			return;
		}

		Object obj = this._targetDataSources.get(dbName);
		if (obj != null && sid.equals(dbName)) {
			return;
		} else {
			BasicDataSource dataSource = this.getDataSource(dbName);
			if (null != dataSource)
				this.setDataSource(dbName, dataSource);
		}
	}

     创建一个数据源:

public BasicDataSource getDataSource(String serverId) {
		return (BasicDataSource) createDataSource(serverId);
	}

	/** 该方法为同步方法,防止并发创建两个相同的数据库 使用双检锁的方式,防止并发 */
	private synchronized DataSource createDataSource(String dbName) {
		String url = String.format(DB_MYSQL_URL, dbName);
		//
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName(DRIVER_CLASS_NAME);
		dataSource.setUrl(url);
		dataSource.setUsername(DB_MYSQL_USERNAME);
		dataSource.setPassword(DB_MYSQL_PASSWORD);
		dataSource.setTestWhileIdle(true);
		dataSource.setValidationQuery("select 1");
		dataSource.setTestOnBorrow(true);

		return dataSource;
	}

       数据库连接信息放在了一个配置文件中,通过注解@Value来获取,访问不同的的数据库只是dbName这个字段不同,所以可以使用String.format(DB_MYSQL_URL, dbName)来修改连接url,访问不同的数据库。

       注解:

@Value("${driverClassName}")
	private String DRIVER_CLASS_NAME;

	@Value("${db.mysql.url}")
	private String DB_MYSQL_URL;

	@Value("${db.mysql.username}")
	private String DB_MYSQL_USERNAME;

	@Value("${db.mysql.password}")
	private String DB_MYSQL_PASSWORD;

配置文件:

# 数据库驱动
driverClassName=com.mysql.cj.jdbc.Driver
# 数据库URL
db.mysql.url=jdbc:mysql://localhost:3306/%s?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL
# 数据库用户名
db.mysql.username.encryption=81A3FD464E18C4497A79CE7CC9D5B660
db.nbx.mysql.username.encryption=81A3FD464E18C4497A79CE7CC9D5B660
# 密码
db.mysql.password.encryption=16CCEF25E22FA42E89821B7B27858DE26DD8BFF1139FF2C70BECCA88E373F809
db.nbx.mysql.password.encryption=16CCEF25E22FA42E89821B7B27858DE26DD8BFF1139FF2C70BECCA88E373F809

     创建好一个数据源后,把它放到集合缓存中,下次就不用重新创建了,同时告诉父类AbstractRoutingDataSource,要使用当前这个数据源访问数据库:

BasicDataSource dataSource = this.getDataSource(dbName);
		if (null != dataSource)
			this.setDataSource(dbName, dataSource);

			
	public void setDataSource(String serverId, BasicDataSource dataSource) {
		this.addTargetDataSource(serverId, dataSource);
		DataSourceContextHolder.setDbName(serverId);
	}

	public void addTargetDataSource(String key, BasicDataSource dataSource) {
		this._targetDataSources.put(key, dataSource);
		this.setTargetDataSources(this._targetDataSources);
	}

	public void setTargetDataSources(Map<Object, Object> targetDataSources) {
		this._targetDataSources = targetDataSources;
		super.setTargetDataSources(this._targetDataSources);
		afterPropertiesSet();
	}

4、    使用Spring的AOP,将线程ThreadLocal的dbName变量清空,防止影响下一次访问数据库。

@Aspect // for aop
@Component // for auto scan
@Order(0) // 在事务前执行
public class DataSourceInterceptor {
	@Pointcut("execution(public * com.bx.erp.action.bo.*.*(..))")
	public void dataSourceSlave() {
	};

	@Before("dataSourceSlave()")
	public void before(JoinPoint jp) {
//		System.out.println("进入切面");
	}

	@After("dataSourceSlave()")
	public void removeDataSoruce(JoinPoint joinPoint) throws Throwable {
		DataSourceContextHolder.clearDbName();
	}

5、    在我们的产品中,不同的公司对应着不同的数据库dbName,所以可以根据dbName创建不同的数据源。

Company company = getCompanyFromSession(session);
	String dbName = company.getDbName();

	DataSourceContextHolder.setDbName(dbName);
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 你可以使用 Python 的 `pymysql` 库来连接和操作 MySQL 数据库。你可以使用如下的代码来实现一个类,用来动态切换 MySQL 数据源: ``` import pymysql class MySQLClient: def __init__(self, host, user, password, database): self.host = host self.user = user self.password = password self.database = database def connect(self): self.connection = pymysql.connect( host=self.host, user=self.user, password=self.password, database=self.database ) self.cursor = self.connection.cursor() def close(self): self.cursor.close() self.connection.close() def change_database(self, database): self.database = database self.connect() ``` 使用这个类时,你可以先初始化一个 MySQLClient 对象,然后使用 `connect` 方法来连接到 MySQL 数据库。当你想要切换数据库时,可以使用 `change_database` 方法来更新数据库名称并重新连接。 例如: ``` mysql_client = MySQLClient('localhost', 'root', 'password', 'database1') mysql_client.connect() # 在 database1 中执行 SQL 语句 mysql_client.cursor.execute('SELECT * FROM table1') # 切换到 database2 mysql_client.change_database('database2') # 在 database2 中执行 SQL 语句 mysql_client.cursor.execute('SELECT * FROM table2') mysql_client.close() ``` 注意:在使用完 MySQLClient 对象之后,应该调用 `close` 方法来关闭数据库连接。 ### 回答2: 要实现一个可以动态切换MySQL数据源的类,可以按照以下几个步骤进行: 1. 使用数据库连接池:首先,需要使用数据库连接池来管理数据库连接资源。常见的数据库连接池有Apache Commons DBCP、C3P0等,选择一个适合的连接池库引入项目中。 2. 配置多个MySQL数据源:在配置文件中,定义多个MySQL数据库的连接信息,包括连接URL、用户名、密码等。根据实际需要,可以配置任意个数的MySQL数据源。 3. 编写动态切换逻辑:在类中,定义一个变量来保存当前正在使用的数据源,初始时可以设置一个默认的数据源。在需要切换数据源的地方,可以通过调用一个方法来更新当前数据源的值。 4. 动态获取数据库连接:在类中,可以定义一个方法用于获取数据库连接。在该方法中,根据当前的数据源值,从数据库连接池中获取对应的连接。 5. 使用数据库连接:通过获取的数据库连接,即可执行各种SQL语句,实现数据库的操作。 需要注意以下几点: - 需要在程序初始化时,加载数据库连接池和配置文件,保证数据源信息被正确加载。 - 切换数据源时,需要先释放当前数据源数据库连接,再获取新数据源数据库连接。 - 注意对数据库连接进行关闭和释放资源的操作,避免资源泄露。 通过以上步骤,可以实现一个可以动态切换MySQL数据源的类。在使用时,只需调用相应的切换方法,即可实现对多个MySQL数据库动态访问。 ### 回答3: 实现一个类,可以动态切换MYSQL数据源,可以通过以下四个步骤来完成: 1. 定义一个 DataSource 类,并在其构造函数中初始化默认的 MYSQL 数据源。 ```java public class DataSource { private String url; private String username; private String password; public DataSource() { this.url = "默认的MYSQL数据源URL"; this.username = "默认的用户名"; this.password = "默认的密码"; } } ``` 2. 在 DataSource 类中定义一个方法 setDataSource(),用于修改当前的 MYSQL 数据源。 ```java public void setDataSource(String url, String username, String password) { this.url = url; this.username = username; this.password = password; } ``` 3. 在需要使用 MYSQL 数据源的地方,通过创建 DataSource 对象,并调用 setDataSource() 方法来切换当前的 MYSQL 数据源。 ```java DataSource dataSource = new DataSource(); dataSource.setDataSource("新的MYSQL数据源URL", "新的用户名", "新的密码"); ``` 4. 在实际使用 MYSQL 数据源的地方,可以使用 DataSource 类中的属性来获取当前的 MYSQL 数据源。 ```java String currentURL = dataSource.url; String currentUsername = dataSource.username; String currentPassword = dataSource.password; ``` 通过以上步骤,就可以实现一个类,可以动态切换 MYSQL 数据源。我们可以根据实际需要,在程序中调用 setDataSource() 方法,更改数据源,并在需要使用数据源的地方访问当前数据源的属性。这样就可以实现动态切换 MYSQL 数据源的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值