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.12.0
2. Usage
The following sections describe how you may use this plugin in a 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.
dataSource {
driverClassName = 'org.h2.Driver'
username = 'sa'
password = ''
pool {
idleTimeout = 60000
maximumPoolSize = 8
minimumIdle = 5
}
}
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
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 |
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
@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.
public interface DataSourceCallback<R> {
R handle(@Nonnull String dataSourceName, @Nonnull DataSource dataSource)
throws SQLException;
}
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
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
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
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-2.1.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:2.1.0'
}
The griffon
plugin will take care of the rest given its configuration.
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>2.1.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>
<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();