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方法中如果擷取本地線程變量中的連接配接位空,那麼就選擇預設資料源。