1. Introduction

The DataSource plugin enables lightweight access multiple SQL datasources. This plugin does NOT provide domain classes nor dynamic finders like GORM does.

Griffon version: 2.2.0

2. Usage

The following sections describe how you may use this plugin in an project.

2.1. Configuration

You must create a configuration file named DataSource that holds the settings for accessing the database. This file follows the same standard configuration mechanism as the application’s Config file, which means you can define the configuration using

  • a properties file

  • a Java file

  • a Groovy script

The following example shows the default settings needed to connect to an H2 database taking into account that each environment may connect to a different database.

src/main/resources/DataSource.groovy
dataSource {
    driverClassName = 'org.h2.Driver'
    username = 'sa'
    password = ''
    pool {
        maxWait = 60000
        maxIdle = 5
        maxActive = 8
    }
}

environments {
    development {
        dataSource {
            dbCreate = 'create' // one of ['create', 'skip']
            url = 'jdbc:h2:mem:@application.name@-dev'
        }
    }
    test {
        dataSource {
            dbCreate = 'create'
            url = 'jdbc:h2:mem:@application.name@-test'
        }
    }
    production {
        dataSource {
            dbCreate = 'skip'
            url = 'jdbc:h2:mem:@application.name@-prod'
        }
    }
}

You may configure multiple named datasources (the default datasource is aptly named default) as the following snippet shows

src/main/resources/DataSource.groovy
dataSources {
    internal {
        driverClassName = 'org.h2.Driver'
        username = 'sa'
        password = ''
        schema = false
        url = 'jdbc:h2:mem:@application.name@-internal'
    }
    people {
        driverClassName = 'org.h2.Driver'
        username = 'sa'
        password = ''
        dbCreate = 'create'
        url = 'jdbc:h2:mem:@application.name@-people'
    }
}

The following properties are optional

Property Type Default Description

connect_on_startup

boolean

false

Establishes a connection to the datasource at the beginning of the Startup phase.

jmx

boolean

true

Expose the connection pool using JMX.

The plugin’s module registers a DataSourceHandler helper class that defines the base contract for accessing a datasource and issue SQL queries to it. This class has the following methods

griffon.plugins.datasource.DataSourceHandler.java
@Nullable
<R> R withDataSource(@Nonnull DataSourceCallback<R> callback) throws RuntimeSQLException;

@Nullable
<R> R withDataSource(@Nonnull String dataSourceName, @Nonnull DataSourceCallback<R> callback)
    throws RuntimeSQLException;

@Nullable
<R> R withConnection(@Nonnull ConnectionCallback<R> callback) throws RuntimeSQLException;

@Nullable
<R> R withConnection(@Nonnull String dataSourceName, @Nonnull ConnectionCallback<R> callback)
    throws RuntimeSQLException;

void closeDataSource();

void closeDataSource(@Nonnull String dataSourceName);

These method are aware of multiple dataSources. If no dataSourceName is specified when calling them then the default dataSource will be selected. You can inject an instance of this class anywhere it’s needed using @Inject. There are two callbacks you may use with these methods: either you work with a java.sql.DataSource instance using DataSourceCallback or with a java.sql.Connection instance using ConnectionCallback. Both options will come in handy with other plugins or APIs that require one instance or the other.

Both callbacks are defined using a functional interface approach, which means you can apply lambda expressions if running with JDK8+ or closures if running Groovy.

griffon.plugins.datasource.DataSourceCallback.java
public interface DataSourceCallback<R> {
    R handle(@Nonnull String dataSourceName, @Nonnull DataSource dataSource)
        throws SQLException;
}
griffon.plugins.datasource.ConnectionCallback.java
public interface ConnectionCallback<R> {
    R handle(@Nonnull String dataSourceName, @Nonnull DataSource dataSource, @Nonnull Connection connection)
        throws SQLException;
}

2.2. Example

The following is a trivial usage of the DataSourceHandler paired with JDBI inside a Java service

com.acme.SampleService.java
package com.acme;

import griffon.core.artifact.GriffonService;
import griffon.metadata.ArtifactProviderFor;
import org.codehaus.griffon.runtime.core.artifact.AbstractGriffonService;

import griffon.plugins.datasource.DataSourceHandler;
import griffon.plugins.datasource.DataSourceCallback;
import javax.sql.DataSource;
import java.sql.SQLException;

import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.util.StringMapper;

import javax.annotation.Nonnull;
import javax.inject.Inject;

@ArtifactProviderFor(GriffonService.class)
public class SampleService extends AbstractGriffonService {
    @Inject
    private DataSourceHandler dataSourceHandler;

    public String getPersonName(final long id) {
         return dataSourceHandler.withDataSource(new DataSourceCallback<String>() {
             public String handle(@Nonnull String dataSourceName, @Nonnull DataSource dataSource)
                            throws SQLException {
                 try(Handle h = new DBI(dataSource).open()) {
                     return h.createQuery("SELECT name FROM people WHERE id = :id")
                         .bind("id", id)
                         .map(StringMapper.FIRST)
                         .first();
                 }
         });
    }
}

Here’s the Groovy version of it

com.acme.SampleService.groovy
package com.acme

import griffon.core.artifact.GriffonService
import griffon.metadata.ArtifactProviderFor

import griffon.plugins.datasource.DataSourceHandler
import javax.sql.DataSource

import org.skife.jdbi.v2.DBI
import org.skife.jdbi.v2.Handle
import org.skife.jdbi.v2.util.StringMapper

import javax.inject.Inject

@ArtifactProviderFor(GriffonService)
class SampleService {
    @Inject
    private DataSourceHandler dataSourceHandler

    String getPersonName(long id) {
         dataSourceHandler.withDataSource { String dataSourceName, DataSource dataSource ->
             try(Handle h = new DBI(dataSource).open()) {
                 h.createQuery('SELECT name FROM people WHERE id = :id')
                        .bind('id', id)
                        .map(StringMapper.FIRST)
                        .first()
             }
         }
    }
}

2.3. Events

The following events will be triggered by DataSourceHandler

DataSourceConnectStart(String dataSourceName, Map<String, Object> config)

Triggered before connecting to the dataSource.

DataSourceConnectEnd(String dataSourceName, Map<String, Object> config, DataSource dataSource)

Triggered after connecting to the dataSource.

DataSourceDisconnectStart(String dataSourceName, Map<String, Object> config, DataSource dataSource)

Triggered before disconnecting from the dataSource.

DataSourceDisconnectEnd(String dataSourceName, Map<String, Object> config)

Triggered after disconnecting from the dataSource.

2.4. AST Transformation

You can apply the @DataSourceAware AST transformation on any class. This injects the behavior of DataSourceHandler into said class. The previous Groovy service example can be rewritten as follows

com.acme.SampleService.groovy
package com.acme

import griffon.core.artifact.GriffonService
import griffon.metadata.ArtifactProviderFor
import griffon.transform.DataSourceAware

import javax.sql.DataSource

import org.skife.jdbi.v2.DBI
import org.skife.jdbi.v2.Handle
import org.skife.jdbi.v2.util.StringMapper

import javax.inject.Inject

@DataSourceAware
@ArtifactProviderFor(GriffonService)
class SampleService {
    String getPersonName(long id) {
         withDataSource { String dataSourceName, DataSource dataSource ->
             try(Handle h = new DBI(dataSource).open()) {
                 h.createQuery('SELECT name FROM people WHERE id = :id')
                        .bind('id', id)
                        .map(StringMapper.FIRST)
                        .first()
             }
         }
    }
}

2.5. DSL Descriptors

This plugin provides DSL descriptors for Intellij IDEA and Eclipse (provided you have the Groovy Eclipse plugin installed). These descriptors are found inside the griffon-datasource-groovy-compile-1.2.0.jar, with locations

  • dsdl/griffon_datasource.dsld

  • gdsl/griffon_datasource.gdsl

3. Build Configuration

3.1. Gradle

You have two options for configuring this plugin: automatic and manual.

3.1.1. Automatic

As long as the project has the org.codehaus.griffon.griffon plugin applied to it you may include the following snippet in build.gradle

dependencies {
    griffon 'org.codehaus.griffon.plugins:griffon-datasource-plugin:1.2.0'
}

The griffon plugin will take care of the rest given its configuration.

3.1.2. Manual

You will need to configure any of the following blocks depending on your setup

dependencies {
    compile 'org.codehaus.griffon.plugins:griffon-datasource-core:1.2.0'
}
Compile Only
dependencies {
    compileOnly 'org.codehaus.griffon.plugins:griffon-datasource-groovy-compile:1.2.0'
}

3.2. Maven

First configure the griffon-datasource-plugin BOM in your POM file, by placing the following snippet before the <build> element

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.griffon.plugins</groupId>
            <artifactId>griffon-datasource-plugin</artifactId>
            <version>1.2.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Next configure dependencies as required by your particular setup

<dependency>
    <groupId>org.codehaus.griffon.plugins</groupId>
    <artifactId>griffon-datasource-core</artifactId>
</dependency>
Provided scope
<dependency>
    <groupId>org.codehaus.griffon.plugins</groupId>
    <artifactId>griffon-datasource-groovy-compile</artifactId>
</dependency>

Don’t forget to configure all -compile dependencies with the maven-surefire-plugin, like so

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <classpathDependencyExcludes>
            <classpathDependencyExclude>
                org.codehaus.griffon:griffon-datasource-groovy-compile
            </classpathDependencyExclude>
        </classpathDependencyExcludes>
    </configuration>
</plugin>

4. Modules

The following sections display all bindings per module. Use this information to successfully override a binding on your own modules or to troubleshoot a module binding if the wrong type has been applied by the Griffon runtime.

4.1. DataSource

Module name: datasource

bind(ResourceBundle.class)
    .withClassifier(named("datasource"))
    .toProvider(new ResourceBundleProvider("DataSource"))
    .asSingleton();

bind(Configuration.class)
    .withClassifier(named("datasource"))
    .to(DefaultDataSourceConfiguration.class)
    .asSingleton();

bind(DataSourceStorage.class)
    .to(DefaultDataSourceStorage.class)
    .asSingleton();

bind(DataSourceFactory.class)
    .to(DefaultDataSourceFactory.class)
    .asSingleton();

bind(DataSourceHandler.class)
    .to(DefaultDataSourceHandler.class)
    .asSingleton();

bind(GriffonAddon.class)
    .to(DataSourceAddon.class)
    .asSingleton();