Grails 2.4.2 - Dynamically referencing default dat

2019-04-10 17:33发布

问题:

This question has been partly answered here but there is still an issue with referencing the default datasource dynamically.

I'm working on an internal application that allows developers to modify configuration settings for one of our multi-tenant applications and push those settings from dev to testing, staging and production. Each one of these will have their own datasource, and the Grails app will be installed on each developer's computer.

The local datasource will be the default one, and then dataSource_testing, dataSource_staging and so on will reference the appropriate environments.

I can dynamically reference the remote datasources with the following:

def setting = Setting."${params.environmnet}".get(id)

However, if params.environment is referencing the default datasource, this code no longer works. In the documentation for setting up domain classes, it states:

If a domain class uses the default DataSource and one or more others, use the special name 'DEFAULT' to indicate the default DataSource

See the documentation

This looks like it works when defining which datasources your domain class is applicable to, but can't reference your domain class like: def setting = Setting.DEFAULT.get(id). You get an error stating:

No such property: DEFAULT for class...

I really really don't want to have to re-engineer the application so that the default datasource isn't really used and then create a new dataSource_local datasource. If I can avoid that, it would be great as it means updating stacks of code that is only applicable to the local datasource.

So my question... is there a way to dynamically reference the default datasource?

回答1:

It's currently not supported, but you can do some hacks to add the missing metamethod (getDEFAULT).

You can do a feature request issue on Grails Jira if you think this is an important feature. It might make sense to support static access (CompileStatic) by adding a separate method for looking up the so called GormStaticApi instance for the given datasource. Please add a feature request to Grails Jira so that it explains your use case.

existing implementation skips the DEFAULT datasource:

The logic for Hibernate4: https://github.com/grails/grails-data-mapping/blob/f9da9fe/grails-datastore-gorm-hibernate4/src/main/groovy/org/codehaus/groovy/grails/orm/hibernate/cfg/HibernateUtils.groovy#L122-L125 and Hibernate3: https://github.com/grails/grails-data-mapping/blob/f9da9fe/grails-datastore-gorm-hibernate/src/main/groovy/org/codehaus/groovy/grails/orm/hibernate/cfg/HibernateUtils.groovy#L121-L124

The workaround is to save this following class in grails-app/conf/WorkaroundsBootStrap:

import org.codehaus.groovy.grails.commons.DomainClassArtefactHandler
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.commons.GrailsClass
import org.codehaus.groovy.grails.commons.GrailsDomainClassProperty
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil
import org.codehaus.groovy.grails.orm.hibernate.cfg.HibernateUtils

class WorkaroundsBootStrap {
    GrailsApplication grailsApplication
    def dataSource
    def transactionManager
    def hibernateDatastore

    def init = { servletContext ->
        def datasourceName = GrailsDomainClassProperty.DEFAULT_DATA_SOURCE
        for(GrailsClass grailsClass in grailsApplication.getArtefacts(DomainClassArtefactHandler.TYPE)) {
            def dc = grailsClass
            if (GrailsHibernateUtil.isMappedWithHibernate(dc) && GrailsHibernateUtil.usesDatasource(dc, datasourceName)) {
                HibernateUtils.registerNamespaceMethods dc, hibernateDatastore, datasourceName, transactionManager, grailsApplication
            }
        }
    }
}