天天看點

mysql多資料源切換_spring+mybatis多資料源動态切換

spring mvc+mybatis+多資料源切換 選取oracle,mysql作為例子切換資料源。oracle為預設資料源,在測試的action中,進行mysql和oracle的動态切換。

web.xml

webAppRootKey

trac

org.springframework.web.util.Log4jConfigListener

CharacterEncodingFilter

org.springframework.web.filter.CharacterEncodingFilter

encoding

utf8

forceEncoding

true

CharacterEncodingFilter

/*

dispatcher

org.springframework.web.servlet.DispatcherServlet

contextConfigLocation

/WEB-INF/dispatcher.xml

1

dispatcher

*.action

org.springframework.web.context.ContextLoaderListener

applicationContext.xml

配置 parentDataSource 的父bean.再配置多個資料源繼承這個父bean,對driverClass,url,username,password,等資料源連接配接參數進行各自的重寫。例如 mySqlDataSource ,在 DataSources bean中注入所有要切換的資料源,并且設定預設的資料源。

DataSourceInstances.java

public class DataSourceInstances{

public static final String MYSQL="MYSQL";

public static final String ORACLE="ORACLE";

}

DataSourceSwitch.java

public class DataSourceSwitch{

private static final ThreadLocal contextHolder=new ThreadLocal();

public static void setDataSourceType(String dataSourceType){

contextHolder.set(dataSourceType);

}

public static String getDataSourceType(){

return (String) contextHolder.get();

}

public static void clearDataSourceType(){

contextHolder.remove();

}

}

DataSources.java

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DataSources extends AbstractRoutingDataSource{

@Override

protected Object determineCurrentLookupKey() {

return DataSourceSwitch.getDataSourceType();

}

}

測試

@Controller

@SuppressWarnings("unused")

public class TestAction {

@Autowired

TestMapper testMapper;

@RequestMapping("/test.action")

public ModelAndView test(

HttpServletRequest request,

HttpServletResponse resp){

ModelAndView model = new ModelAndView("test");

model.addObject("test1", "這是一個測試,擷取預設資料連接配接MYSQL:"+testMapper.test());

DataSourceSwitch.setDataSourceType(DataSourceInstances.ORACLE);

model.addObject("test2", "這是一個測試,擷取資料連接配接ORACLE:"+testMapper.test());

DataSourceSwitch.setDataSourceType(DataSourceInstances.MYSQL);

model.addObject("test3", "這是一個測試,擷取資料連接配接MYSQL:"+testMapper.test());

return model;

}

}

代碼解釋:

檢視AbstractRoutingDataSource中的擷取資料庫連接配接源碼

publicConnection getConnection()throwsSQLException

{returndetermineTargetDataSource().getConnection();

}

檢視determineTargetDataSource方法

protected DataSource determineTargetDataSource()

{

Assert.notNull(resolvedDataSources, "DataSource router not initialized");

Object lookupKey = determineCurrentLookupKey();

DataSource dataSource = (DataSource)resolvedDataSources.get(lookupKey);

if(dataSource == null && (lenientFallback || lookupKey == null))

dataSource = resolvedDefaultDataSource;

if(dataSource == null)

throw new IllegalStateException((new StringBuilder()).append("Cannot determine target DataSource for lookup key [").append(lookupKey).append("]").toString());

else

return dataSource;

}

其中DataSource dataSource = (DataSource)resolvedDataSources.get(lookupKey); 中的resolvedDataSources 就是我們spring中設定的targetDataSources,是一個Map類型,裡面有我們設定的MYSQL和ORACLE資料庫連接配接池

注意determineCurrentLookupKey方法,

protected abstract Object determineCurrentLookupKey();

是一個抽象方法,需要我們去實作,我們将資料源對應的KEY放在本地線程中,那麼可以随時在代碼中進行切換資料源

預設資料源

在spring配置檔案中,我們将defaultTargetDataSource注入到AbstractRoutingDataSource中

public voidafterPropertiesSet()

{if(targetDataSources == null)throw new IllegalArgumentException("Property 'targetDataSources' is required");

resolvedDataSources= newHashMap(targetDataSources.size());

Object lookupKey;

DataSource dataSource;for(Iterator iterator =targetDataSources.entrySet().iterator(); iterator.hasNext(); resolvedDataSources.put(lookupKey, dataSource))

{

java.util.Map.Entry entry=(java.util.Map.Entry)iterator.next();

lookupKey=resolveSpecifiedLookupKey(entry.getKey());

dataSource=resolveSpecifiedDataSource(entry.getValue());

}if(defaultTargetDataSource != null)

resolvedDefaultDataSource=resolveSpecifiedDataSource(defaultTargetDataSource);

}

AbstractRoutingDataSource類實作了InitializingBean接口,項目啟動會實作方法afterPropertiesSet,生成resolvedDefaultDataSource執行個體,這樣在determineTargetDataSource方法中如果擷取本地線程變量中的連接配接位空,那麼就選擇預設資料源。