最近要为公司多个游戏做类似的统计功能,考虑到模块的复用性,决定做个动态数据源,根据不同的游戏参数切换不同的datasource。
网上查了下,spring2.0以后增加了AbstractRoutingDataSource这个东西。下面是实现方法
首先看下AbstractRoutingDataSource类结构,继承了AbstractDataSource
- public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
- }
既然是AbstractDataSource,当然就是javax.sql.DataSource的子类,于是我们自然地回去看它的getConnection方法:
- public Connection getConnection() throws SQLException {
- return determineTargetDataSource().getConnection();
- }
原来奥妙就在determineTargetDataSource()里:
- /**
- * Retrieve the current target DataSource. Determines the
- * {@link #determineCurrentLookupKey() current lookup key}, performs
- * a lookup in the {@link #setTargetDataSources targetDataSources} map,
- * falls back to the specified
- * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
- * @see #determineCurrentLookupKey()
- */
- protected DataSource determineTargetDataSource() {
- Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
- Object lookupKey = determineCurrentLookupKey();
- DataSource dataSource = (DataSource) this.resolvedDataSources.get(lookupKey);
- if (dataSource == null) {
- dataSource = this.resolvedDefaultDataSource;
- }
- if (dataSource == null) {
- throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
- }
- return dataSource;
- }
这里用到了我们需要进行实现的抽象方法determineCurrentLookupKey(),该方法返回需要使用的DataSource的key值,然后根据这个key从resolvedDataSources这个map里取出对应的DataSource,如果找不到,则用默认的resolvedDefaultDataSource
- <bean id="onlineDynamicDataSource" class="com.xx.stat.base.dynamic.DynamicDataSource">
- <property name="targetDataSources">
- <map key-type="java.lang.String">
- <entry key="xx" value-ref="dataSourceXX"/>
- <entry key="yy" value-ref="dataSourceYY"/>
- </map>
- </property>
- <property name="defaultTargetDataSource" ref="dataSource"/>
- </bean>
观察上面的配置文件,发现我们配置的是targetDataSources和defaultTargetDataSource
- public void afterPropertiesSet() {
- if (this.targetDataSources == null) {
- throw new IllegalArgumentException("targetDataSources is required");
- }
- this.resolvedDataSources = new HashMap(this.targetDataSources.size());
- for (Iterator it = this.targetDataSources.entrySet().iterator(); it.hasNext();) {
- Map.Entry entry = (Map.Entry) it.next();
- Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
- DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
- this.resolvedDataSources.put(lookupKey, dataSource);
- }
- if (this.defaultTargetDataSource != null) {
- this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
- }
- }
下面就是我们自己实现的子类DynamicDataSource
- public class DynamicDataSource extends AbstractRoutingDataSource{
- @Override
- public void setTargetDataSources(Map targetDataSources) {
- super.setTargetDataSources(targetDataSources);
- }
- @Override
- public Object unwrap(Class iface) throws SQLException {
- return null;
- }
- @Override
- public boolean isWrapperFor(Class iface) throws SQLException {
- return false;
- }
- @Override
- protected Object determineCurrentLookupKey() {
- String dataSourceName = DynamicDataSourceHolder.getDataSourceName();
- return dataSourceName;
- }
DynamicDataSourceHolder(这个东西要在调用数据库前初始化,mybatis 中可以在 org.mybatis.spring.SqlSessionFactoryBean 的plugins中用拦截器做,)
- public class DynamicDataSourceHolder {
- private static final ThreadLocal<String> holder = new ThreadLocal<String>();
- public static void putDataSourceName(String name){
- holder.set(name);
- }
- public static String getDataSourceName(){
- return holder.get();
- }
- }