当项目发展到一定阶段,就需要对数据库进行一定的优化。一般会对数据库进行横向和纵向切库分表,但是这样的问题就来了,在我们操作数据库时,需要根据切分规则提前获得我们需要的数据库的连接,这明显会加重程序员的负担。
比如我们将“用户信息数据库”按照用户注册的年月来分库,在用户注册的时候,为用户分配一个以yyyyMM开头的唯一标示,以方便我们能快速定位到切分后的子数据库。那么问题来了,我们在项目中,如何动态且方便的获得我们需要的数据源呢?Spring提供了一个解决方案,那就是基于AbstractRoutingDataSource的动态数据源切换。
AbstractRoutingDataSource的类结构:
想要使用,只需要重写determineCurrentLookupKey方法,在说明他的作用之前,先看下调用他的位置:
private Map
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
可以看到,determineCurrentLookupKey的返回值会作为Map的key来查找数据库连接。而determineTargetDataSource方法通常是用来返回给调用端DataSource,因此我们可以通过重写这两个方法,来实现动态切换数据库。
首先定义一个Bean来保存数据库信息,也将它作为Map的key,数据库连接作为Value。
public class DatabaseDefineBean {
private String userName;
private String passWord;
private String url;
...getter/setter
}
编写我们自己的数据源
public class MyDataSource extends AbstractRoutingDataSource {
private String driverClassName;
static ThreadLocal<DatabaseDefineBean> defineBeans = new ThreadLocal<DatabaseDefineBean>();
@Override
protected Object determineCurrentLookupKey() {
return defineBeans.get();
}
@Override
protected DataSource determineTargetDataSource() {
DriverManagerDataSource dataSource = getDataSource((DatabaseDefineBean) determineCurrentLookupKey());
return dataSource;
}
private DriverManagerDataSource getDataSource(DatabaseDefineBean databse) {
DriverManagerDataSource dataSource = new DriverManagerDataSource(databse.getUrl(), databse.getUserName(),
databse.getPassWord());
return dataSource;
}
}
这里使用Spring+Mybatis测试(源码及数据库文件下载):
String userName = "writeuser";
String passWord = "writeuser";
String url = "jdbc:mysql://192.168.1.61:3306/DataBaseRoute";
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"/com/smart/config/spring-jdbc.xml"});
MyDataSource.setDefineBeans(new DatabaseDefineBean(userName, passWord, url));
DataSource dataSource = context.getBean(DataSource.class);
System.out.println(dataSource);
SqlSessionTemplate sessionTemplate = context.getBean(SqlSessionTemplate.class);
DatabaseInfoMapper mapper = sessionTemplate.getSqlSessionFactory().openSession().getMapper(DatabaseInfoMapper.class);
List<?> list = mapper.selectAll();
System.out.println(list);
}
那么如何动态切换呢?这里使用到了ThreadLocal,用于为每个线程保存一个副本,我们只需在操作数据库之前设置一下Database的基本信息就可以轻松获得想要的数据源了。
MyDataSource.setDefineBeans(new DatabaseDefineBean(userName, passWord, url));
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-09-29 01:00:16