Last updated by scryan7371 3 years ago

Single Tenant Database Set Up

The single-tenant option is a little more immature than the multi-tenant option. It inherits all datasource configuration from the default datasource in Datasource.groovy, and allows you to provide a custom datasource url per tenant. This means that you can't mix and match drivers or user/pass combos for different tenants in the same instance.

TenantUtils.doWithTenant and currentTenant.set() do not work in single tenant mode, unless you manually create a new hibernate session and bind it to the current thread.

Also, normal second-level caches will not work in single-tenant mode, as you will get primary key collisions across databases. You'll need to use a multi-tenant wrapper for your cache implementation (see below)

The plugin supports the following types of datasources:

  • jndi
  • pooled (dbcp)
  • regular (DriverManagerDataSource)
  • TransactionAwareDataSourceProxy (Default for 1.3.X JNDI support only for now.)

Resolving the DataSource

Note: Everywhere in the documentation that you see a datasource url, you can also use a jndiName. If the system encounters a tenant you haven't mapped, it will use the default datasource configuration in Datasource.groovy. This includes all database operations that happen before grails is completely loaded.

Mapping DataSources through Config.groovy

To map datasource urls in Config.groovy, add the following code:

tenant {
    mode = "singleTenant"

datasourceResolver.type = "config" //This is the default and can be omitted

dataSourceTenantMap { //The "t" before the tenantId is required because you can't have a //variable that's a number t1 = "jdbc:mysql://localhost/ets_dev1" t2 = "jdbc:mysql://localhost/ets_dev2" t3 = "jdbc:mysql://localhost/ets_dev3"

//JNDI Example t1 = "java:comp/env/myDataSource" } }

Mapping DataSources through the database

Mapping data sources through the database allows them to be provisioned on the fly without any restarting or reloading.

Add the following line to Config.groovy

tenant {
    mode = "singleTenant"

datasourceResolver.type = "db" }

Then run the following command:

grails create-data-source-map
This will create a domain class used to store the mappings (tenant.DataSourceTenantMap). You can add a new mapping on the fly like this:

def dsMap = new DataSourceTenantMap()
dsMap.mappedTenantId = 5
dsMap.dataSource = "jdbc:mysql://myserver/customerdb"
//Jndi Example
//dsMap.dataSource = "java:comp/env/myDataSource"
dsMap.save()

When the record is saved, the datasource resolver will be automatically updated, and the app can start accessing the new datasource immediately

2nd-Level Caching

As mentioned above, regular 2nd level caches will not work in single-tenant mode, as you will get primary key collisions across databases. You will need to use a special wrapped version of the cache that makes it tenant-aware. The wrapped versions should maintain all your settings, but each tenant will maintain its OWN cache instances. This means that if can't simply set cache settings to be applied to the entire JVM; you'll have to 'divide' the settings among tenants. To set this update your datasource.groovy file to set

cache.provider_class = 'grails.plugin.multitenant.oscache.cache.MultiTenantOSCacheProvider'

or

cache.provider_class = 'grails.plugin.multitenant.ehcache.cache.MultiTenantEhCacheProvider'

OSCache

If you use the OpenSymphony cache provider (org.hibernate.cache.OSCacheProvider), you should use grails.plugin.multitenant.oscache.cache.MultiTenantOSCacheProvider instead.

EHCache(New with 0.18)

If you are using the EHCache cache provider your should use grails.plugin.multitenant.ehcache.cache.MultiTenantEhCacheProvider insead.