2015-11-03 156 views

回答

0

在我的应用程序中,我需要能够访问多个数据库,并且我在启动时没有连接到数据库的字符串,因此我无法将它们预先配置为数据源。

我所用的策略是如下:

1)实现,其存储数据源的集合的abstractroutingdatasource。它还使用DataSource {}接受了一个全局闭包,这是一个可接受的参数和代码块。这些参数是数据源的名称和连接信息。在withDataSource方法中,它会创建一个新的DataSource并使用其键将其保存到地图中,或使用地图上的保存。

2)在我的客户端代码中,我会使用withDataSource闭包来访问我需要的任何数据库。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 
import org.springframework.context.ApplicationContextAware 
import org.springframework.beans.factory.DisposableBean 
import org.springframework.beans.factory.InitializingBean 
import org.springframework.context.ApplicationContext 
import javax.sql.DataSource 
import org.springframework.jdbc.datasource.DriverManagerDataSource 
import org.springframework.jdbc.datasource.lookup.DataSourceLookup 
import org.springframework.jdbc.datasource.SimpleDriverDataSource 

class SwitchableDataSource 
       extends AbstractRoutingDataSource 
       implements ApplicationContextAware, 
         InitializingBean, 
         DisposableBean 
         { 

    ApplicationContext applicationContext 

    Map _targetDataSources 
    private ThreadLocal<String> currentDataSource = new ThreadLocal<String>() 

    void afterPropertiesSet(){ 
    super.afterPropertiesSet() 
    // register global closure for setting the current datasource 
    // ... should probably be restricted to services and controllers. 
    Object.metaClass.withDataSource = this.&executeWithDataSource 
    } 

    public synchronized def executeWithDataSource(def params, def method){ 

     // if datasource doesn't exist, register new one in application context 
     // set a threadlocal key for the current datasource key 
     def dataSourceName = "" 
     def key = params.clientId.toString() 

     if(!_targetDataSources.containsKey(key)){ 

     dataSourceName = "dataSource_${params.clientId}".toString() 
     applicationContext.registerSingleton(dataSourceName, 
       //org.apache.commons.dbcp.BasicDataSource, 
       org.springframework.jdbc.datasource.DriverManagerDataSource, 
       new org.springframework.beans.MutablePropertyValues(
       [ 
       driverClassName : 'com.mysql.jdbc.Driver', 
       username : params.username.toString(), 
       password : params.password.toString(), 
       url : params.connectionString.toString() 
       ] 
      ) 
     ) 

     // add datasource to map 
     _targetDataSources[key] = applicationContext.getBean(dataSourceName) 
     targetDataSources = _targetDataSources 
     super.afterPropertiesSet() // AbstractRoutingDataSource is lame... it converts 'targetDataSource' into 'resolvedDataSource' in afterPropertiesSet() 
     } 

     // set the thread local variable for the current datasource 
     currentDataSource.set(params.clientId.toString()) 

     // call the closure that was passed in containing some data access code 
     // and return whatever it returns 
     return method() 
    } 

    void destroy(){ 
    // remove global closure 
    Object.metaClass.withDataSource = null // may need to throw a missing method exception 
    } 

    protected Object determineCurrentLookupKey() { 
    (currentDataSource.get()?:"DEFAULT_DATASOURCE").toString() 
    } 
} 

2)在客户端代码,拨打电话如下:

withDataSource([ clientId: client.id, // client.id was my key 
     connectionString: jdbcConnection.url, 
     username: jdbcConnection.username, 
     password: jdbcConnection.password ]) { 

     SomeDomainObject.list() 
     SomeDomainObject.delete(); 
    } 

3)记住,在你的resources.groovy春天来注册你的路由数据源。请注意,它分配了一个默认数据源,因此初始数据源列表不为空。

// Place your Spring DSL code here 
beans = { 

    dataSource_defaultClientDb(org.apache.commons.dbcp.BasicDataSource) { bean -> 
    bean.singleton = true 
    driverClassName = 'com.mysql.jdbc.Driver' 
    username = '${defaultClientDB_username}' 
    password = '${defaultClientDB_password}' 
    url = '${defaultClientDB_url}' 
    } 

    dataSource_clientdb(SwitchableDataSource){ bean -> 
    bean.singleton = true 
    _targetDataSources = ["DEFAULT_DATASOURCE":ref("dataSource_defaultClientDb")] 
    targetDataSources = _targetDataSources 
    } 

    lobHandlerDetector_clientdb(org.springframework.jdbc.support.lob.DefaultLobHandler) 

} 

对于您的应用程序,你可能并不需要使用withDataSource方法为我所用 - 你只需要与switchabledatasource注册您联系,并告诉它如何查找它应该使用哪些数据源,例如,您可以为每个请求的线程本地数据分配一些ID,并且switchabledatasource可以使用相同的线程本地值来确定要使用哪个数据源。

我希望这至少能让你朝着正确的方向前进。