我似乎找不到任何关于 Grails Multitenancy with Multiple Databases(每个租户一个)的插件或示例。我正在使用 grails 2.4.0 。谁能帮我?
最佳答案
在我的应用程序中,我需要能够访问多个数据库,并且在启动时我没有到数据库的连接字符串,因此我无法将它们预配置为数据源。
我使用的策略如下:
1) 实现一个存储数据源集合的抽象路由数据源。它还用DataSource{}注册了一个全局闭包,一个接受的一些参数和代码块。参数是数据源的名称和连接信息。在 withDataSource 方法中,它要么创建一个新的 DataSource 并使用其键将其保存到 map 中,要么使用 map 中保存的内容。
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中用spring注册你的路由数据源。请注意,它分配了一个默认数据源,因此初始数据源列表不为空。
// 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 方法 - 您只需要注册与可切换数据源的连接并告诉它如何查找它应该使用的数据源,例如您可以分配一些 ID每个请求上的线程本地数据和可切换数据源可以使用相同的线程本地值来确定要使用哪个数据源。
我希望这至少能让你朝着正确的方向前进。
关于具有多个数据库的 Grails Multi-Tenancy (每个租户一个)2.4.0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33496684/