Error occurred running Grails CLI: startup failed:script14738267015581837265078.groovy: 13: unable to resolve class com.foo.Bar
4 Configuration
Version: 5.0.0
Table of Contents
4 Configuration
It may seem odd that in a framework that embraces "convention-over-configuration" that we tackle this topic now. With Grails' default settings you can actually develop an application without doing any configuration whatsoever, as the quick start demonstrates, but it’s important to learn where and how to override the conventions when you need to. Later sections of the user guide will mention what configuration settings you can use, but not how to set them. The assumption is that you have at least read the first section of this chapter!
4.1 Basic Configuration
Configuration in Grails is generally split across 2 areas: build configuration and runtime configuration.
Build configuration is generally done via Gradle and the build.gradle
file. Runtime configuration is by default specified in YAML in the grails-app/conf/application.yml
file.
If you prefer to use Grails 2.0-style Groovy configuration then it is possible to specify configuration using Groovy’s ConfigSlurper syntax. Two Groovy configuration files are available: grails-app/conf/application.groovy
and grails-app/conf/runtime.groovy
:
-
Use
application.groovy
for configuration that doesn’t depend on application classes -
Use
runtime.groovy
for configuration that does depend on application classes
This separation is necessary because configuration values defined in |
For Groovy configuration the following variables are available to the configuration script:
Variable | Description |
---|---|
userHome |
Location of the home directory for the account that is running the Grails application. |
grailsHome |
Location of the directory where you installed Grails. If the |
appName |
The application name as it appears in build.gradle. |
appVersion |
The application version as it appears in build.gradle. |
For example:
my.tmp.dir = "${userHome}/.grails/tmp"
Accessing Configuration with GrailsApplication
If you want to read runtime configuration settings, i.e. those defined in application.yml
, use the grailsApplication object, which is available as a variable in controllers and tag libraries:
class MyController {
def hello() {
def recipient = grailsApplication.config.getProperty('foo.bar.hello')
render "Hello ${recipient}"
}
}
The config
property of the grailsApplication
object is an instance of the Config interface and provides a number of useful methods to read the configuration of the application.
In particular, the getProperty
method (seen above) is useful for efficiently retrieving configuration properties, while specifying the property type (the default type is String) and/or providing a default fallback value.
class MyController {
def hello(Recipient recipient) {
//Retrieve Integer property 'foo.bar.max.hellos', otherwise use value of 5
def max = grailsApplication.config.getProperty('foo.bar.max.hellos', Integer, 5)
//Retrieve property 'foo.bar.greeting' without specifying type (default is String), otherwise use value "Hello"
def greeting = grailsApplication.config.getProperty('foo.bar.greeting', "Hello")
def message = (recipient.receivedHelloCount >= max) ?
"Sorry, you've been greeted the max number of times" : "${greeting}, ${recipient}"
}
render message
}
}
Notice that the Config
instance is a merged configuration based on Spring’s PropertySource concept and reads configuration from the environment, system properties and the local application configuration merging them into a single object.
GrailsApplication
can be easily injected into services and other Grails artifacts:
import grails.core.*
class MyService {
GrailsApplication grailsApplication
String greeting() {
def recipient = grailsApplication.config.getProperty('foo.bar.hello')
return "Hello ${recipient}"
}
}
GrailsConfigurationAware Interface
Accessing configuration dynamically at runtime can have a small effect on application performance. An alternative approach is to implement the GrailsConfigurationAware interface, which provides a setConfiguration
method that accepts the application configuration as a parameter when the class is initialized. You can then assign relevant configuration properties to instance properties on the class for later usage.
The Config
instance has the same properties and usage as the injected GrailsApplication
config object. Here is the service class from the previous example, using GrailsConfigurationAware
instead of injecting GrailsApplication
:
import grails.core.support.GrailsConfigurationAware
class MyService implements GrailsConfigurationAware {
String recipient
String greeting() {
return "Hello ${recipient}"
}
void setConfiguration(Config config) {
recipient = config.getProperty('foo.bar.hello')
}
}
Spring Value Annotation
You can use Spring’s Value annotation to inject configuration values:
import org.springframework.beans.factory.annotation.*
class MyController {
@Value('${foo.bar.hello}')
String recipient
def hello() {
render "Hello ${recipient}"
}
}
In Groovy code you must use single quotes around the string for the value of the Value annotation otherwise it is interpreted as a GString not a Spring expression.
|
As you can see, when accessing configuration settings you use the same dot notation as when you define them.
4.1.1 Options for the YML format Config
The application.yml
file was introduced in Grails 3.0, and YAML is now the preferred format for configuration files.
Using system properties / command line arguments
Suppose you are using the JDBC_CONNECTION_STRING
command line argument and you want to access the same in the yml file then it can be done in the following manner:
production:
dataSource:
url: '${JDBC_CONNECTION_STRING}'
Similarly system arguments can be accessed.
You will need to have this in build.gradle
to modify the bootRun
target if grails run-app
is used to start the application
bootRun {
systemProperties = System.properties
}
For testing the following will need to change the test
task as follows
test {
systemProperties = System.properties
}
External configuration
Grails will read application.(properties|yml)
from the ./config
or the current directory by default.
As Grails is a SpringBoot configuration options are available as well, for documentation please consult: https://docs.spring.io/spring-boot/docs/2.5.2/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files
4.1.2 Built in options
Grails has a set of core settings that are worth knowing about. Their defaults are suitable for most projects, but it’s important to understand what they do because you may need one or more of them later.
Runtime settings
On the runtime front, i.e. grails-app/conf/application.yml
, there are quite a few more core settings:
-
grails.enable.native2ascii
- Set this to false if you do not require native2ascii conversion of Grails i18n properties files (default: true). -
grails.views.default.codec
- Sets the default encoding regime for GSPs - can be one of 'none', 'html', or 'base64' (default: 'none'). To reduce risk of XSS attacks, set this to 'html'. -
grails.views.gsp.encoding
- The file encoding used for GSP source files (default: 'utf-8'). -
grails.mime.file.extensions
- Whether to use the file extension to dictate the mime type in Content Negotiation (default: true). -
grails.mime.types
- A map of supported mime types used for Content Negotiation. -
grails.serverURL
- A string specifying the server URL portion of absolute links, including server name e.g. grails.serverURL="http://my.yourportal.com". See createLink. Also used by redirects. -
grails.views.gsp.sitemesh.preprocess
- Determines whether SiteMesh preprocessing happens. Disabling this slows down page rendering, but if you need SiteMesh to parse the generated HTML from a GSP view then disabling it is the right option. Don’t worry if you don’t understand this advanced property: leave it set to true. -
grails.reload.excludes
andgrails.reload.includes
- Configuring these directives determines the reload behavior for project specific source files. Each directive takes a list of strings that are the class names for project source files that should be excluded from reloading behavior or included accordingly when running the application in development with therun-app
command. If thegrails.reload.includes
directive is configured, then only the classes in that list will be reloaded.
4.1.3 Logging
Since Grails 3.0, logging is handled by the Logback logging framework and can be configured with the grails-app/conf/logback.groovy
file.
If you prefer XML you can replace the logback.groovy file with a logback.xml file instead.
|
For more information on configuring logging refer to the Logback documentation on the subject.
4.1.3.1 Logger Names
Grails artifacts (controllers, services …) get injected a log
property automatically.
Prior to Grails 3.3.0, the name of the
logger for Grails Artifact followed the convention grails.app.<type>.<className>
, where type is the
type of the artifact, for example, controllers
or services
, and className
is the fully
qualified name of the artifact.
Grails 3.3.x simplifies logger names. The next examples illustrate the changes:
BookController.groovy
located at grails-app/controllers/com/company
NOT annotated with @Slf4j
Logger Name (Grails 3.3.x or higher) |
Logger Name (Grails 3.2.x or lower) |
|
|
BookController.groovy
located at grails-app/controllers/com/company
annotated with @Slf4j
Logger Name (Grails 3.3.x or higher) |
Logger Name (Grails 3.2.x or lower) |
|
|
BookService.groovy
located at grails-app/services/com/company
NOT annotated with @Slf4j
Logger Name (Grails 3.3.x or higher) |
Logger Name (Grails 3.2.x or lower) |
|
|
BookService.groovy
located at grails-app/services/com/company
annotated with @Slf4j
Logger Name (Grails 3.3.x or higher) |
Logger Name (Grails 3.2.x or lower) |
|
|
BookDetail.groovy
located at src/main/groovy/com/company
annotated with @Slf4j
Logger Name (Grails 3.3.x or higher) |
Logger Name (Grails 3.2.x or lower) |
|
|
4.1.3.2 Masking Request Parameters From Stacktrace Logs
When Grails logs a stacktrace, the log message may include the names and values of all of the request parameters for the current request.
To mask out the values of secure request parameters, specify the parameter names in the grails.exceptionresolver.params.exclude
config property:
grails:
exceptionresolver:
params:
exclude:
- password
- creditCard
Request parameter logging may be turned off altogether by setting the grails.exceptionresolver.logRequestParameters
config property to false. The default value is true when the application is running in DEVELOPMENT mode and false for all other
environments.
grails:
exceptionresolver:
logRequestParameters: false
4.1.3.3 External Configuration File
If you set the configuration property logging.config
, you can instruct Logback
to use an external configuration file.
logging:
config: /Users/me/config/logback.groovy
Alternatively, you can supply the configuration file location with a system property:
$ ./gradlew -Dlogging.config=/Users/me/config/logback.groovy bootRun
Or, you could use an environment variable:
$ export LOGGING_CONFIG=/Users/me/config/logback.groovy
$ ./gradlew bootRun
4.1.4 GORM
Grails provides the following GORM configuration options:
-
grails.gorm.failOnError
- If set totrue
, causes thesave()
method on domain classes to throw agrails.validation.ValidationException
if validation fails during a save. This option may also be assigned a list of Strings representing package names. If the value is a list of Strings then the failOnError behavior will only be applied to domain classes in those packages (including sub-packages). See the save method docs for more information.
For example, to enable failOnError for all domain classes:
grails:
gorm:
failOnError: true
and to enable failOnError for domain classes by package:
grails:
gorm:
failOnError:
- com.companyname.somepackage
- com.companyname.someotherpackage
4.1.5 Configuring an HTTP proxy
To setup Grails to use an HTTP proxy there are two steps. Firstly you need to configure the grails
CLI to be aware of the proxy if you wish to use it to create applications and so on. This can be done using the GRAILS_OPTS
environment variable, for example on Unix systems:
export GRAILS_OPTS="-Dhttps.proxyHost=127.0.0.1 -Dhttps.proxyPort=3128 -Dhttp.proxyUser=test -Dhttp.proxyPassword=test"
The default profile repository is resolved over HTTPS so https.proxyPort and https.proxyUser are used, however the username and password are specified with http.proxyUser and http.proxyPassword
|
For Windows systems the environment variable can be configured under My Computer/Advanced/Environment Variables
.
With this configuration in place the grails
command can connect and authenticate via a proxy.
Secondly, since Grails uses Gradle as the build system, you need to configure Gradle to authenticate via the proxy. For instructions on how to do this see the Gradle user guide section on the topic.
4.2 The Application Class
Every new Grails application features an Application
class within the grails-app/init
directory.
The Application
class subclasses the GrailsAutoConfiguration class and features a static void main
method, meaning it can be run as a regular application.
4.2.1 Executing the Application Class
There are several ways to execute the Application
class, if you are using an IDE then you can simply right click on the class and run it directly from your IDE which will start your Grails application.
This is also useful for debugging since you can debug directly from the IDE without having to connect a remote debugger when using the run-app --debug-jvm
command from the command line.
You can also package your application into a runnable WAR file, for example:
$ grails package
$ java -jar build/libs/myapp-0.1.war
This is useful if you plan to deploy your application using a container-less approach.
4.2.2 Customizing the Application Class
There are several ways in which you can customize the Application
class.
Customizing Scanning
By default Grails will scan all known source directories for controllers, domain class etc., however if there are packages in other JAR files you wish to scan you can do so by overriding the packageNames()
method of the Application
class:
class Application extends GrailsAutoConfiguration {
@Override
Collection<String> packageNames() {
super.packageNames() + ['my.additional.package']
}
...
}
Registering Additional Beans
The Application
class can also be used as a source for Spring bean definitions, simply define a method annotated with the Bean and the returned object will become a Spring bean. The name of the method is used as the bean name:
class Application extends GrailsAutoConfiguration {
@Bean
MyType myBean() {
return new MyType()
}
...
}
4.2.3 The Application LifeCycle
The Application
class also implements the GrailsApplicationLifeCycle interface which all plugins implement.
This means that the Application
class can be used to perform the same functions as a plugin. You can override the regular plugins hooks such as doWithSpring
, doWithApplicationContext
and so on by overriding the appropriate method:
class Application extends GrailsAutoConfiguration {
@Override
Closure doWithSpring() {
{->
mySpringBean(MyType)
}
}
...
}
4.3 Environments
Per Environment Configuration
Grails supports the concept of per environment configuration. The application.yml
and application.groovy
files in the grails-app/conf
directory can use per-environment configuration using either YAML or the syntax provided by ConfigSlurper. As an example consider the following default application.yml
definition provided by Grails:
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
test:
dataSource:
dbCreate: update
url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
production:
dataSource:
dbCreate: update
url: jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
properties:
jmxEnabled: true
initialSize: 5
...
The above can be expressed in Groovy syntax in application.groovy
as follows:
dataSource {
pooled = false
driverClassName = "org.h2.Driver"
username = "sa"
password = ""
}
environments {
development {
dataSource {
dbCreate = "create-drop"
url = "jdbc:h2:mem:devDb"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:h2:mem:testDb"
}
}
production {
dataSource {
dbCreate = "update"
url = "jdbc:h2:prodDb"
properties {
jmxEnabled = true
initialSize = 5
}
}
}
}
Notice how the common configuration is provided at the top level and then an environments
block specifies per environment settings for the dbCreate
and url
properties of the DataSource
.
Packaging and Running for Different Environments
Grails' command line has built in capabilities to execute any command within the context of a specific environment. The format is:
grails <<environment>> <<command name>>
In addition, there are 3 preset environments known to Grails: dev
, prod
, and test
for development
, production
and test
. For example to create a WAR for the test
environment you would run:
grails test war
To target other environments you can pass a grails.env
variable to any command:
grails -Dgrails.env=UAT run-app
Programmatic Environment Detection
Within your code, such as in a Gant script or a bootstrap class you can detect the environment using the Environment class:
import grails.util.Environment
...
switch (Environment.current) {
case Environment.DEVELOPMENT:
configureForDevelopment()
break
case Environment.PRODUCTION:
configureForProduction()
break
}
Per Environment Bootstrapping
It’s often desirable to run code when your application starts up on a per-environment basis. To do so you can use the grails-app/init/BootStrap.groovy
file’s support for per-environment execution:
def init = { ServletContext ctx ->
environments {
production {
ctx.setAttribute("env", "prod")
}
development {
ctx.setAttribute("env", "dev")
}
}
ctx.setAttribute("foo", "bar")
}
Generic Per Environment Execution
The previous BootStrap
example uses the grails.util.Environment
class internally to execute. You can also use this class yourself to execute your own environment specific logic:
Environment.executeForCurrentEnvironment {
production {
// do something in production
}
development {
// do something only in development
}
}
4.4 The DataSource
Since Grails is built on Java technology setting up a data source requires some knowledge of JDBC (the technology that stands for Java Database Connectivity).
If you use a database other than H2 you need a JDBC driver. For example for MySQL you would need Connector/J.
Drivers typically come in the form of a JAR archive. It’s best to use the dependency resolution to resolve the jar if it’s available in a Maven repository, for example you could add a dependency for the MySQL driver like this:
dependencies {
runtime 'mysql:mysql-connector-java:5.1.29'
}
Once you have the JAR resolved you need to get familiar with how Grails manages its database configuration. The configuration can be maintained in either grails-app/conf/application.groovy
or grails-app/conf/application.yml
. These files contain the dataSource definition which includes the following settings:
-
driverClassName
- The class name of the JDBC driver -
username
- The username used to establish a JDBC connection -
password
- The password used to establish a JDBC connection -
url
- The JDBC URL of the database -
dbCreate
- Whether to auto-generate the database from the domain model - one of 'create-drop', 'create', 'update', 'validate', or 'none' -
pooled
- Whether to use a pool of connections (defaults to true) -
logSql
- Enable SQL logging to stdout -
formatSql
- Format logged SQL -
dialect
- A String or Class that represents the Hibernate dialect used to communicate with the database. See the org.hibernate.dialect package for available dialects. -
readOnly
- Iftrue
makes the DataSource read-only, which results in the connection pool callingsetReadOnly(true)
on eachConnection
-
transactional
- Iffalse
leaves the DataSource’s transactionManager bean outside the chained BE1PC transaction manager implementation. This only applies to additional datasources. -
persistenceInterceptor
- The default datasource is automatically wired up to the persistence interceptor, other datasources are not wired up automatically unless this is set totrue
-
properties
- Extra properties to set on the DataSource bean. See the Tomcat Pool documentation. There is also a Javadoc format documentation of the properties. -
jmxExport
- Iffalse
, will disable registration of JMX MBeans for all DataSources. By default JMX MBeans are added for DataSources withjmxEnabled = true
in properties. -
type
- The connection pool class if you want to force Grails to use it when there are more than one available.
A typical configuration for MySQL in application.groovy
may be something like:
dataSource {
pooled = true
dbCreate = "update"
url = "jdbc:mysql://localhost:3306/my_database"
driverClassName = "com.mysql.jdbc.Driver"
dialect = org.hibernate.dialect.MySQL5InnoDBDialect
username = "username"
password = "password"
type = "com.zaxxer.hikari.HikariDataSource"
properties {
jmxEnabled = true
initialSize = 5
maxActive = 50
minIdle = 5
maxIdle = 25
maxWait = 10000
maxAge = 10 * 60000
timeBetweenEvictionRunsMillis = 5000
minEvictableIdleTimeMillis = 60000
validationQuery = "SELECT 1"
validationQueryTimeout = 3
validationInterval = 15000
testOnBorrow = true
testWhileIdle = true
testOnReturn = false
jdbcInterceptors = "ConnectionState;StatementCache(max=200)"
defaultTransactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED
}
}
When configuring the DataSource do not include the type or the def keyword before any of the configuration settings as Groovy will treat these as local variable definitions and they will not be processed. For example the following is invalid: |
dataSource {
boolean pooled = true // type declaration results in ignored local variable
...
}
Example of advanced configuration using extra properties:
dataSource {
pooled = true
dbCreate = "update"
url = "jdbc:mysql://localhost:3306/my_database"
driverClassName = "com.mysql.jdbc.Driver"
dialect = org.hibernate.dialect.MySQL5InnoDBDialect
username = "username"
password = "password"
type = "com.zaxxer.hikari.HikariDataSource"
properties {
// Documentation for Tomcat JDBC Pool
// http://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html#Common_Attributes
// https://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/tomcat/jdbc/pool/PoolConfiguration.html
jmxEnabled = true
initialSize = 5
maxActive = 50
minIdle = 5
maxIdle = 25
maxWait = 10000
maxAge = 10 * 60000
timeBetweenEvictionRunsMillis = 5000
minEvictableIdleTimeMillis = 60000
validationQuery = "SELECT 1"
validationQueryTimeout = 3
validationInterval = 15000
testOnBorrow = true
testWhileIdle = true
testOnReturn = false
ignoreExceptionOnPreLoad = true
// http://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html#JDBC_interceptors
jdbcInterceptors = "ConnectionState;StatementCache(max=200)"
defaultTransactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED // safe default
// controls for leaked connections
abandonWhenPercentageFull = 100 // settings are active only when pool is full
removeAbandonedTimeout = 120
removeAbandoned = true
// use JMX console to change this setting at runtime
logAbandoned = false // causes stacktrace recording overhead, use only for debugging
// JDBC driver properties
// Mysql as example
dbProperties {
// Mysql specific driver properties
// http://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html
// let Tomcat JDBC Pool handle reconnecting
autoReconnect=false
// truncation behaviour
jdbcCompliantTruncation=false
// mysql 0-date conversion
zeroDateTimeBehavior='convertToNull'
// Tomcat JDBC Pool's StatementCache is used instead, so disable mysql driver's cache
cachePrepStmts=false
cacheCallableStmts=false
// Tomcat JDBC Pool's StatementFinalizer keeps track
dontTrackOpenResources=true
// performance optimization: reduce number of SQLExceptions thrown in mysql driver code
holdResultsOpenOverStatementClose=true
// enable MySQL query cache - using server prep stmts will disable query caching
useServerPrepStmts=false
// metadata caching
cacheServerConfiguration=true
cacheResultSetMetadata=true
metadataCacheSize=100
// timeouts for TCP/IP
connectTimeout=15000
socketTimeout=120000
// timer tuning (disable)
maintainTimeStats=false
enableQueryTimeouts=false
// misc tuning
noDatetimeStringSync=true
}
}
}
More on dbCreate
Hibernate can automatically create the database tables required for your domain model. You have some control over when and how it does this through the dbCreate
property, which can take these values:
-
create - Drops the existing schema and creates the schema on startup, dropping existing tables, indexes, etc. first.
-
create-drop - Same as create, but also drops the tables when the application shuts down cleanly.
-
update - Creates missing tables and indexes, and updates the current schema without dropping any tables or data. Note that this can’t properly handle many schema changes like column renames (you’re left with the old column containing the existing data).
-
validate - Makes no changes to your database. Compares the configuration with the existing database schema and reports warnings.
-
any other value - does nothing
Setting the dbCreate
setting to "none" is recommended once your schema is relatively stable and definitely when your application and database are deployed in production. Database changes are then managed through proper migrations, either with SQL scripts or a migration tool like Flyway or Liquibase. The Database Migration plugin uses Liquibase.
4.4.1 DataSources and Environments
The previous example configuration assumes you want the same config for all environments: production, test, development etc.
Grails' DataSource definition is "environment aware", however, so you can do:
dataSource {
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
dialect = org.hibernate.dialect.MySQL5InnoDBDialect
// other common settings here
}
environments {
production {
dataSource {
url = "jdbc:mysql://liveip.com/liveDb"
// other environment-specific settings here
}
}
}
4.4.2 Automatic Database Migration
The dbCreate
property of the DataSource
definition is important as it dictates what Grails should do at runtime with regards to automatically generating the database tables from GORM classes. The options are described in the DataSource section:
-
create
-
create-drop
-
update
-
validate
-
no value
In development mode dbCreate
is by default set to "create-drop", but at some point in development (and certainly once you go to production) you’ll need to stop dropping and re-creating the database every time you start up your server.
It’s tempting to switch to update
so you retain existing data and only update the schema when your code changes, but Hibernate’s update support is very conservative. It won’t make any changes that could result in data loss, and doesn’t detect renamed columns or tables, so you’ll be left with the old one and will also have the new one.
Grails supports migrations with Liquibase or Flyway via plugins.
4.4.3 Transaction-aware DataSource Proxy
The actual dataSource
bean is wrapped in a transaction-aware proxy so you will be given the connection that’s being used by the current transaction or Hibernate Session
if one is active.
If this were not the case, then retrieving a connection from the dataSource
would be a new connection, and you wouldn’t be able to see changes that haven’t been committed yet (assuming you have a sensible transaction isolation setting, e.g. READ_COMMITTED
or better).
4.4.4 Database Console
The H2 database console is a convenient feature of H2 that provides a web-based interface to any database that you have a JDBC driver for, and it’s very useful to view the database you’re developing against. It’s especially useful when running against an in-memory database.
You can access the console by navigating to http://localhost:8080/h2-console in a browser. See the Spring Boot H2 Console Documentation for more information on the options available.
4.4.5 Multiple Datasources
By default all domain classes share a single DataSource
and a single database, but you have the option to partition your domain classes into two or more data sources.
Configuring Additional DataSources
The default DataSource
configuration in grails-app/conf/application.yml
looks something like this:
dataSource:
pooled: true
jmxExport: true
driverClassName: org.h2.Driver
username: sa
password:
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
test:
dataSource:
dbCreate: update
url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
production:
dataSource:
dbCreate: update
url: jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
properties:
jmxEnabled: true
initialSize: 5
This configures a single DataSource
with the Spring bean named dataSource
. To configure extra data sources, add a dataSources
block (at the top level, in an environment block, or both, just like the standard DataSource
definition) with a custom name. For example, this configuration adds a second DataSource
, using MySQL in the development environment and Oracle in production:
dataSource:
pooled: true
jmxExport: true
driverClassName: org.h2.Driver
username: sa
password:
dataSources:
lookup:
dialect: org.hibernate.dialect.MySQLInnoDBDialect
driverClassName: com.mysql.jdbc.Driver
username: lookup
password: secret
url: jdbc:mysql://localhost/lookup
dbCreate: update
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
test:
dataSource:
dbCreate: update
url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
production:
dataSource:
dbCreate: update
url: jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
properties:
jmxEnabled: true
initialSize: 5
...
dataSources:
lookup:
dialect: org.hibernate.dialect.Oracle10gDialect
driverClassName: oracle.jdbc.driver.OracleDriver
username: lookup
password: secret
url: jdbc:oracle:thin:@localhost:1521:lookup
dbCreate: update
You can use the same or different databases as long as they’re supported by Hibernate.
If you need to inject the lookup
datasource in a Grails artefact, you can do it like this:
DataSource dataSource_lookup
While defining multiple data sources, one of them must be named "dataSource". This is required because Grails determines which data source is the default by determining which one is named "dataSource". |
Configuring Domain Classes
If a domain class has no DataSource
configuration, it defaults to the standard 'dataSource'
. Set the datasource
property in the mapping
block to configure a non-default DataSource
. For example, if you want to use the ZipCode
domain to use the 'lookup'
DataSource
, configure it like this:
class ZipCode {
String code
static mapping = {
datasource 'lookup'
}
}
A domain class can also use two or more data sources. Use the datasources
property with a list of names to configure more than one, for example:
class ZipCode {
String code
static mapping = {
datasources(['lookup', 'auditing'])
}
}
If a domain class uses the default DataSource
and one or more others, use the special name 'DEFAULT'
to indicate the default DataSource
:
class ZipCode {
String code
static mapping = {
datasources(['lookup', 'DEFAULT'])
}
}
If a domain class uses all configured data sources, use the special value 'ALL'
:
class ZipCode {
String code
static mapping = {
datasource 'ALL'
}
}
Namespaces and GORM Methods
If a domain class uses more than one DataSource
then you can use the namespace implied by each DataSource
name to make GORM calls for a particular DataSource
. For example, consider this class which uses two data sources:
class ZipCode {
String code
static mapping = {
datasources(['lookup', 'auditing'])
}
}
The first DataSource
specified is the default when not using an explicit namespace, so in this case we default to 'lookup'
. But you can call GORM methods on the 'auditing' DataSource
with the DataSource
name, for example:
def zipCode = ZipCode.auditing.get(42)
...
zipCode.auditing.save()
As you can see, you add the DataSource
to the method call in both the static case and the instance case.
Hibernate Mapped Domain Classes
You can also partition annotated Java classes into separate datasources. Classes using the default datasource are registered in grails-app/conf/hibernate.cfg.xml
. To specify that an annotated class uses a non-default datasource, create a hibernate.cfg.xml
file for that datasource with the file name prefixed with the datasource name.
For example if the Book
class is in the default datasource, you would register that in grails-app/conf/hibernate.cfg.xml
:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
'-//Hibernate/Hibernate Configuration DTD 3.0//EN'
'http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd'>
<hibernate-configuration>
<session-factory>
<mapping class='org.example.Book'/>
</session-factory>
</hibernate-configuration>
and if the Library
class is in the "ds2" datasource, you would register that in grails-app/conf/ds2_hibernate.cfg.xml
:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
'-//Hibernate/Hibernate Configuration DTD 3.0//EN'
'http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd'>
<hibernate-configuration>
<session-factory>
<mapping class='org.example.Library'/>
</session-factory>
</hibernate-configuration>
The process is the same for classes mapped with hbm.xml files - just list them in the appropriate hibernate.cfg.xml file.
Services
Like Domain classes, by default Services use the default DataSource
and PlatformTransactionManager
. To configure a Service to use a different DataSource
, use the static datasource
property, for example:
class DataService {
static datasource = 'lookup'
void someMethod(...) {
...
}
}
A transactional service can only use a single DataSource
, so be sure to only make changes for domain classes whose DataSource
is the same as the Service.
Note that the datasource specified in a service has no bearing on which datasources are used for domain classes; that’s determined by their declared datasources in the domain classes themselves. It’s used to declare which transaction manager to use.
If you have a Foo
domain class in dataSource1
and a Bar
domain class in dataSource2
, if WahooService
uses dataSource1
, a service method that saves a new Foo
and a new Bar
will only be transactional for Foo
since they share the same datasource. The transaction won’t affect the Bar
instance. If you want both to be transactional you’d need to use two services and XA datasources for two-phase commit, e.g. with the Atomikos plugin.
Transactions across multiple data sources
Grails does not by default try to handle transactions that span multiple data sources.
You can enable Grails to use the Best Effort 1PC pattern for handling transactions across multiple datasources. To do so you must set the grails.transaction.chainedTransactionManagerPostProcessor.enabled
setting to true
in application.yml
:
grails:
transaction:
chainedTransactionManagerPostProcessor:
enabled: true
The Best Efforts 1PC pattern is fairly general but can fail in some circumstances that the developer must be aware of.
This is a non-XA pattern that involves a synchronized single-phase commit of a number of resources. Because the 2PC is not used, it can never be as safe as an XA transaction, but is often good enough if the participants are aware of the compromises.
The basic idea is to delay the commit of all resources as late as possible in a transaction so that the only thing that can go wrong is an infrastructure failure (not a business-processing error). Systems that rely on Best Efforts 1PC reason that infrastructure failures are rare enough that they can afford to take the risk in return for higher throughput. If business-processing services are also designed to be idempotent, then little can go wrong in practice.
The BE1PC implementation was added in Grails 2.3.6. . Before this change additional datasources didn’t take part in transactions initiated in Grails. The transactions in additional datasources were basically in auto commit mode. In some cases this might be the wanted behavior. One reason might be performance: on the start of each new transaction, the BE1PC transaction manager creates a new transaction to each datasource. It’s possible to leave an additional datasource out of the BE1PC transaction manager by setting transactional = false
in the respective configuration block of the additional dataSource. Datasources with readOnly = true
will also be left out of the chained transaction manager (since 2.3.7).
By default, the BE1PC implementation will add all beans implementing the Spring PlatformTransactionManager
interface to the chained BE1PC transaction manager. For example, a possible JMSTransactionManager
bean in the Grails application context would be added to the Grails BE1PC transaction manager’s chain of transaction managers.
You can exclude transaction manager beans from the BE1PC implementation with this configuration option:
grails:
transaction:
chainedTransactionManagerPostProcessor:
enabled: true
blacklistPattern: '.*'
The exclude matching is done on the name of the transaction manager bean. The transaction managers of datasources with transactional = false
or readOnly = true
will be skipped and using this configuration option is not required in that case.
XA and Two-phase Commit
When the Best Efforts 1PC pattern isn’t suitable for handling transactions across multiple transactional resources (not only datasources), there are several options available for adding XA/2PC support to Grails applications.
The Spring transactions documentation contains information about integrating the JTA/XA transaction manager of different application servers. In this case, you can configure a bean with the name transactionManager
manually in resources.groovy
or resources.xml
file.
4.5 Versioning
Detecting Versions at Runtime
You can detect the application version using Grails' support for application metadata using the GrailsApplication class. For example within controllers there is an implicit grailsApplication variable that can be used:
def version = grailsApplication.metadata.getApplicationVersion()
You can retrieve the version of Grails that is running with:
def grailsVersion = grailsApplication.metadata.getGrailsVersion()
or the GrailsUtil
class:
import grails.util.GrailsUtil
...
def grailsVersion = GrailsUtil.grailsVersion
4.6 Dependency Resolution
Dependency resolution is handled by the Gradle build tool, all dependencies are defined in the build.gradle
file. Refer to the Gradle user guide for more information.