A Grails Plugin for Multiple DataSources

There have been a few requests on the Grails user mailing list about using multiple data sources in a Grails app, i.e. some domain classes use one data source and database and others use another. Grails doesn’t directly support this – there’s only one DataSource and one SessionFactory, and all domain classes use them. But it turns out it’s not that difficult to support this (and it doesn’t involve too many ugly hacks …)


There are some implications of the approach I took. This doesn’t provide XA transactions, 2PC, etc. It’s just a partitioning of classes between two or more datasources. The way it works is to run after the HibernateGrailsPlugin and DomainClassPlugin have done their work. Then it uses a configuration defined in grails-app/conf/Datasources.groovy and creates one or more extra DataSource, SessionFactory, TransactionManager, etc. and re-runs the HibernateGrailsPlugin‘s doWithDynamicMethods closure for the appropriate subset of domain classes. This way when you call a magic GORM method (e.g. list(), get(), findByNameAndDate(), etc.) it will use the correct underlying datasource. Any domain class not defined as using a secondary datasource will use the ‘core’ datasource defined in DataSource.groovy.

Another issue is that all domain classes stay defined in the core datasource/SessionFactory – the existing behavior isn’t changed, other than redefining the metaclass methods to use another datasource. The only effect of this is that if you use dsCreate = ‘create-drop’ or ‘create’ or ‘update’ for the core datasource, all tables will be created in the core database even though some won’t be used.

Datasources DSL

The DSL used in Datasources.groovy is very similar to the format of DataSource.groovy. One difference is that the ‘hibernate’ section is inside the ‘datasource’ section, and there are a few extra attributes.

Name Type Required Description
name String yes datasource name, used as a Spring bean suffix, e.g. ‘ds2’
readOnly boolean no, defaults to false if true, the datasource and corresponding transactional services will be read-only
driverClassName String yes same as in DataSource
url String yes same as in DataSource
username String no same as in DataSource
password String no same as in DataSource
dbCreate String yes same as in DataSource
dialect String or Class yes (no autodetect) same as in DataSource
jndiName String no same as in DataSource
pooled boolean no, defaults to false same as in DataSource
loggingSql boolean no, defaults to false same as in DataSource
logSql boolean no, defaults to false same as in DataSource
environments List<String> no, defaults to [‘development’, ‘test’, ‘production’] list of environments this DataSource should be active in
domainClasses List<String> or List<Class> yes the domain classes that should use this DataSource
services List<String> no short names of the services that should use this DataSource (same as Spring bean without ‘Service’, e.g. ‘user’ for UserService)

See the sample app (link below) for a usage example.

OpenSessionInView

An OpenSessionInViewInterceptor is defined for each datasource, so the features that it provides are available to all domain classes. For example you can load a domain instance and set a property, and it will be detected as dirty and pushed to the database. Also, lazy loaded collections will load since there’s an active session available.

Further, if you save, create, load, etc. domain instances from multiple datasources in one controller method, all will work fine.

Transactional Services

By default, any service defined as transactional will use the core datasource. If you want a service to use a specific datasource, add its name to the ‘services’ attribute for a datasource definition. If there’s no one datasource for a particular service, you can still define programmatic transactions using withTransaction on any domain class for the appropriate datasource for each method or code block.

HibernateTemplate

I can’t think of non-contrived reasons to do so, but it’s possible to use a domain class in two or more datasources. The problem here is that the metaclass methods will end up mapped to the last declared datasource, so there’s no way to use GORM for the other datasource(s). However you can use Spring’s HibernateTemplate yourself – it has a lot of the functionality of GORM (GORM uses it under the hood). You can use the convenience method DatasourcesUtils.newHibernateTemplate(String dsName) to create a HibernateTemplate configured with the SessionFactory for the named datasource.

Usage

You might want to use this plugin even if you have only one database. Since you can define a datasource as being read-only and point read-only domain classes at it, your prevent yourself from accidentally creating, deleting, or updating instances.

I’ve created a basic (and rather contrived) test application. It has three datasources and five domain classes:

  • Country, State
  • Library, Book
  • Visit

Country and State are just lookup tables, so they use a read-only datasource. Visit has a weak foreign key to Library, but this is not enforced since they’re stored in two databases. It’s the responsibility of the application to ensure that the library id for each visit is valid. This is to simulate having a second database for auditing.

To test the app:

If you want to manually manage the tables in each database instead of letting Hibernate do it for you, you can use the schema-export script from here to capture the DDL for all the tables. Then you can execute the DDL statements for each database separately.


The plugin is in the Grails repository, so to use it just run “grails install-plugin datasources”. You can download the test application here.

76 Responses to “A Grails Plugin for Multiple DataSources”

  1. rene says:

    Hi Burt,
    Is there a possibility to auto-inject datasources defined in DataSources.groovy?
    I would like to use something like this for a read-only database access without domain classes
    class MyFancySqlController {
    def ds2 // like Spring-Bean? “ds2” is auto-injected
    def list = {
    sql = “select * from test_table”
    def db = new Sql(ds2)
    def result = db.rows(sql)

    [ result: result ]
    }
    }

    This will end up in a null pointer exception because ds2 has not been auto-injected as excpected by me

    regards rene

    • Burt says:

      @rene the bean name would be ‘dataSource_ds2’, so use ‘def dataSource_ds2’. You can also use DatasourcesUtils.getDataSource(‘ds2’)

  2. rene says:

    Hi Burt,

    thank you for your quick reply. It works perfectly!
    Greetings from Switzerland
    rene

  3. rene says:

    Hi Burt,

    thank you for your quick reply. It works just perfectly!
    Greetings from Switzerland
    rene

  4. Tom Greasley says:

    Hello Burt,

    Thanks for your blog, amazing source of grails info.

    I’m using your DataSources plugin with an application built using Grails and Mule ESB.

    I’ve used a technique similar to the BackgroundThread and Executor Plugins to ensure a hibernate session is bound to the various threads that mule creates to perform it’s tasks. i.e.:

    http://github.com/basejump/grails-executor/blob/master/src/groovy/grails/plugin/executor/SessionBinderUtils.groovy

    However, I’m running into an issue where UniqueConstraint throws a null pointer exception at line 141 – i.e. the application context is null.

    I believe that I’m not setting up the threads correctly so that they will work with your plugin.

    It is enough to bind sessions created with the original grails session or do you think I need to do more?

    Tom

  5. jon says:

    Hi great plug-in. What i need also is to dynamically change a jndi name from a user selecting a system from a gsp page that is associated with a jndi name.
    Would i try to amend the datasources URL dynamically?

  6. ballistic_realm says:

    Hi Burt

    nice plugin

    btw i have a question
    You said that
    ‘There are some implications of the approach I took. This doesn’t provide XA transactions, 2PC, etc’

    So how to rollback the first datasource sql statement if the second one throwx exception?

    regards

  7. Spider123 says:

    Hi Burt,

    i ve used your plugin in my app. But i have following problems:

    1. I have 2 Datasources, they named db1 and db2.
    2. I defined db1’s config in Datasource.groovy and db2 in Datasources.groovy.
    3. I have 3 Domain Classes, they named DM1.groovy, DM2.groovy and DM3.groovy. DM1.groovy and DM2.groovy are from db1. And DM3.groovy from db2
    4. I started my app and DM3.groovy is created in db1. That is my problem.

    My Datasources.groovy:
    datasources = {
    datasource(name: ‘db2’) {
    domainClasses([DM3])
    readOnly(true)
    driverClassName(‘com.mysql.jdbc.Driver’)
    url(‘jdbc:mysql://localhost:3306/abc?zeroDateTimeBehavior=convertToNull’)
    username(‘u1’)
    password(‘p1’)
    dbCreate(‘update’)
    logSql(true)
    //dialect(org.hibernate.dialect.MySQL5InnoDBDialect)
    hibernate {
    cache {
    use_second_level_cache(false)
    use_query_cache(false)
    }
    }
    }
    }

    thx for your help!

    • eduardo says:

      Hi,

      did you figure it out?

      It has another side effect. I can list the objects from the second db but, since objects point to the first db have references to objects in the second it throws “Cannot add or update a child row: a foreign key constraint fails”

      My app has a Lead domain object in the main db and it points to Region in another one.

      Is there a way to get it to work?

      Eduardo

  8. DerbyD says:

    Hi all,

    I would like to know if there is a way to separate the writes and the
    reads in the database, in the same class.

    How the plugin handle the situation when in the same clase, we want to
    write in one datasource, and the reads directed to another, if possible.

    Thanks for your help,

  9. Peter says:

    From looking at the code it looks like you are using HibernateTransactionManager. Could you make this configurable so we can use something like JOTM instead to give us XA ability?

    • Burt says:

      I’m in the process of merging the plugin into Grails core for 1.4 and in the process will add XA support.

  10. Kishore says:

    Hi Burt,

    I have deployed an application which have 10 domains. My application have many customers. And I do not want to use same database for all customers.
    So, I will create different database (with same domains) as many number of customers.

    I don’t think I can use datasources plugin for this.
    Can you suggest any solution for this?

    Regards,

    kishore

  11. David says:

    Hi,

    We are having the same problem as Tom.

    We are using the Datasources plugin with a Grails 1.3.7 application that is using Executors to distribute the processing and DB save of about 2000 items using 10 threads. Each of the items gets its own job using executor.submit where executor is an ExecutorService. For several hundred of the items, the Grails library file UniqueConstraint.java throws a null pointer exception at line 141 – i.e., the application context is null. The other items are processed without problems.

    The DB save in this case is to our main DB so the plugin is not required for the save. If we remove the plugin from our project the problem goes away. Or if we keep the plugin but stop using threads, the problem goes away. We’d like to keep the plugin and keep using threads, though.

    Any suggestions?

    Thanks!
    David

  12. Hi Burt, I’m using the datasources plugin in my grails 1.3.7 application, my problem is in the domain classes wich uses the default datasource (the datasource defined in Datasource.groovy), when I try to call a hql query like Domainclasss.findAll(“from DomainCalss d where d.property = :mapEntry”,[mapEntry: someObject]) the application crashes and I get an exception like “org.springframework.orm.hibernate3.HibernateQueryException: DomainClass is not mapped …” when I disable the datasources defined in Datasources.groovy the application work’s fine. I don’t know why the app is not mapping the domain classes only when I use the hql querys, by the othre hand if I use dynamyc finders like DomainClass.findAllByProperty(someObject) it work’s fine and even if I use deprecated querys like DomainClass.findAll(“from DomainClass d where d = ?”,someObject) it works fine. Any Idea?

  13. Luciano Araujo says:

    I’m working with the Datasources Plugin in a Grails application so I can access a mySQL and a Microsoft SQL Server database in this application and it’s working perfectly, you really made a great job here.
    However, the server where the SQL Server database is located can be, sometimes, down. So what I have to do is somehow make my application be independent of it, I mean, even if the server is down, the application has to start. In order to make this I simply did a try-catch statement:

    datasources = {

    try{
    datasource(name:’tri’){
    readOnly(true)
    driverClassName(‘net.sourceforge.jtds.jdbc.Driver’)
    url(‘jdbc:jtds:sqlserver://:1433/TRI’)
    username(‘XX’)
    password(‘XX’)
    logSql(true)

    hibernate {
    cache {
    use_second_level_cache(false)
    use_query_cache(false)
    }
    }
    }
    }

    catch(Exception e){
    println(“no connection”)
    }

    }

    The problem is I can only get the application running if the SQL server is up. How can I solve this problem? Is there a way to start the program even if the datasource inside the Datasources.groovy is down?

  14. Paul Kelly says:

    Hi,

    I came across the plugin, but I’m getting the following exception (see below) when I use it. It looks like it’s having problems with two transaction managers.

    Any help much appreciated.

    Paul

    Running Grails application..
    methods for plugin [hibernate:1.4.0.M1]: No unique bean of type [org.springframework.orm.hibernate3.HibernateTransactionManager] is defined: expected single bean but found 2: transactionManager,transactionManager_additional_datasource
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.orm.hibernate3.HibernateTransactionManager] is defined: expected single bean but found 2: transactionManager,transactionManager_additional_datasource

    • Burt says:

      The Datasources plugin doesn’t work in 1.4 or 2.0, but it’s been merged into core for 2.0M1, so you can switch to that since you’re already at 1.4M1.

  15. David says:

    Hi,

    I see there is a Datasources plugin patch from a user which might fix the problem Tom and I reported above when using Datasources with Executor.

    http://jira.grails.org/browse/GPDATASOURCES-26

    I haven’t tested this plugin and I don’t know what the performance implications of the synchronization are.

  16. André Buzzo says:

    Hi, great work with the plugin.
    I need to develop a workaround to get the users from one application to another, just to read and authenticate, but I don’t wanna replicate the entire db.
    The application A is at a database, and B application at another, but not at the same host.
    Is there a way to do this using this plugin? Or do you know or think of some solution to this particular case?
    I’m newbie at grails.

  17. JohnP says:

    Hi Burt,

    Great job with the plugin!

    Our application will have 1 master write/read database, and several read-only databases (which get replicated from the master). For our services when we do any write methods we want to use the write/read datasource with our transaction, otherwise use the read-only datasource. I’m hoping we can achieve this with your plugin – do you have any suggestions? Guess one way is we could have separate read and write services – but would be nice to have them all in one service.

    Still pretty new to grails so sorry for any stupid questions.

    Thanks!

  18. rick says:

    I have an easy question. How can I set the properties for pool size etc? I’ve tried a few things and can’t stumble across how to hook into it despite all my googling.

    rick

  19. David says:

    I learned today that Grails 2.0 has built-in multiple data source support which hopefully fixes the problem Tom and I reported above. That’s good because I’m wary of the potential performance implications of the patch I linked to.

    • Burt says:

      Right – although the multiple datasources support in 2.0 is pretty much just a port of the plugin into the core, the unique constraint handling is done differently and won’t be affected by that issue.

  20. sandy says:

    Hi Burt,

    I have a requirement where I have to add data sources dynamically and then query them. As per my understanding DataSource plugin only works for static data sources configured in your data source files. Is there a way to add the data sources dynamically and use the GORM methods on the domain classes?

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.