Skip to main content
Blog

Grails multi tenant databases – update for 3.2

By 24 november 2016januari 30th, 2017No Comments

Multi-tenant Grails revisited: update to Grails 3.2

Before reading any of this, please know that a full article was published recently about multi tenancy with Grails 3.1.

The key of multi tenancy is that the data handled should ‘stick‘ to a certain tenant, primarily used for white label product situations, or storage divided by region because of its size.

One of the problems there though, was that GORM did not play nice with having multiple data sources. Well, things have changed!

With the release of GORM 6, that integrates nicely with Grails 3.2.x, Grails now has excellent support for multi tenancy. This is largely because of the support by the underlying ORM framework Hibernate which was updated to major version 5.

gorm: blue or bluey-grey (Cairngorms ‘blue stones’)

Gorm: blue or bluey-grey (Cairngorms ‘blue stones’)

Some things remain the same

For groovy Sql support, you will still need to create your own switching data source as before. The previous article covers that as well. There’s just a nice little trick that makes configuration a little less error prone.

The following code just takes whatever data sources that have been configured, logs what it finds, and wires them up to a switching data source.

beans = {
    
    def dataSource = grailsApplication.config.dataSource
    println "INFO: (single) dataSource definition: ${dataSource}"
    
    def dataSources = grailsApplication.config.dataSources
    println "INFO: dataSources definitions: ${dataSources}"
    
    def dsBeans = dataSources.keySet().collectEntries { name ->
        if (name == "dataSource") {
            [(name):ref(name)]
        } else {
            [(name):ref("dataSource_${name}")]
        }
    }
    println "INFO: found dataSource beans: ${dsBeans}"
    
    switchingDataSource(nl.first8.SwitchingDataSource) {
        targetDataSources = dsBeans
        defaultTargetDataSource = ref('dataSource')
    }
    
}

Setting up Grails with GORM 6

To work with GORM 6, the project dependencies need to be setup properly using Gradle.

buildscript {
    ext {
        grailsVersion = project.grailsVersion
    }
    repositories {
        mavenLocal()
        maven { url "https://repo.grails.org/grails/core" }
    }
    dependencies {
        classpath "org.grails:grails-gradle-plugin:$grailsVersion"

        // hibernate5 with GORM 6!
        classpath "org.grails.plugins:hibernate5:6.0.2"
        classpath "org.grails.plugins:views-gradle:1.1.1"
    }
}

// --- most of the build file is left out here ----//

dependencies {
    
    // ----- skipping the boring parts ----//
 
    // we need hibernate 5.x!   
    compile "org.hibernate:hibernate-core:5.1.2.Final"
    compile "org.hibernate:hibernate-ehcache:5.1.2.Final"
}

 

Configuring GORM for multiple databases

We configure application.yml for GORM multi tenancy, switching on databases in our case.

We provide our own implementation, much like the switching data source that was previously used for groovy Sql.

grails:
    gorm:
        failOnError: true
        multiTenancy:
            mode: DATABASE
            tenantResolverClass: 
                nl.first8.MyCustomTenantResolver

The customer resolver (note that are a few simple ones ready to use in the ‘web’ component)

class MyCustomTenantResolver implements TenantResolver {

    @Override
    public Serializable resolveTenantIdentifier() throws TenantNotFoundException {
        def dataSourceId = SelectedTenantHolder.tenantId
        if (!dataSourceId) {
            throw new TenantNotFoundException("No tenant selected.")
        }
        return dataSourceId
    }
    
}

One big catch

Though the default data source bean is named ‘dataSource’, the tenantId should be ‘DEFAULT’. This is not at all obvious from reading the documentation (or grails sources for that matter). You may have noticed the trickery in the code examples earlier. Our customer resolved will return ‘DEFAULT’, from the thread local, when the default data source named ‘dataSource’ should be selected.

For others it’s just the name that was defined in the data sources config. For instance, a data source named ‘foo’ produces a bean named ‘dataSource_foo’ and the tenantId will be ‘foo’.

 

All is well

With Grails 3.2, multi tenancy is again fully supported like it was with Grails 2.x. It’s great to see that the framework is flexible enough, building on powerful open source projects, to provide all the features that even the most complex cases require.

 

Extra

 

Next

Stay tuned for in depth solutions like: having a trigger-driven primary key with Oracle 12 and still have Hibernate/GORM understand identity.