基于AbstractRoutingDataSource的动态切换数据库

来源:互联网 发布:超级基因优化液第二部 编辑:程序博客网 时间:2024/06/04 18:38

当项目发展到一定阶段,就需要对数据库进行一定的优化。一般会对数据库进行横向和纵向切库分表,但是这样的问题就来了,在我们操作数据库时,需要根据切分规则提前获得我们需要的数据库的连接,这明显会加重程序员的负担。
比如我们将“用户信息数据库”按照用户注册的年月来分库,在用户注册的时候,为用户分配一个以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));
0 0
原创粉丝点击