Search

Dark theme | Light theme

November 9, 2015

Ratpacked: Using PostgreSQL Database

Ratpack is a lean framework. To add extra functionality, like using a database, we can use framework modules. Ratpack comes with a couple of framework modules. One of the modules is the SqlModule. This module adds a Groovy Sql instance to our application. We can use it to execute SQL code against a database. The SqlModule needs a DataSource instance, so we need to write some code to provide a DataSource instance for a PostgreSQL database.

First we add the JDBC drivers for PostgreSQL to our Gradle build file:

// File: build.gradle
...
dependencies {
    ...
    compile 'org.postgresql:postgresql:9.4-1205-jdbc42'
    ...
}
...

Next we create a configurable module to provide a DataSource instance for our application. We write a class with configuration properties and a module class that uses the class with configuration properties.

// File: src/main/groovy/com/mrhaki/ratpack/postgres/PostgresConfig.groovy
package com.mrhaki.ratpack.postgres

import groovy.transform.CompileStatic

@CompileStatic
class PostgresConfig {
    String user
    String password
    String serverName = 'localhost'
    String databaseName
    Integer portNumber = 5432
}
// File: src/main/groovy/com/mrhaki/ratpack/postgres/PostgresModule.groovy
package com.mrhaki.ratpack.postgres

import com.google.inject.Provides
import groovy.transform.CompileStatic
import org.postgresql.ds.PGSimpleDataSource
import ratpack.guice.ConfigurableModule

import javax.sql.DataSource

/**
 * Module to create DataSource instance for PostgreSQL database.
 */
@CompileStatic
class PostgresModule extends ConfigurableModule<PostgresConfig> {

    @Override
    protected void configure() {
    }

    /**
     * Create DataSource instance.
     *
     * @param config Configuration object with properties for creating DataSource.
     * @return DataSource for connecting to PostgreSQL database.
     */
    @Provides
    DataSource dataSource(final PostgresConfig config) {
        createDataSource(config)
    }

    /**
     * Subclasses can override this method to create other DataSource
     * instance, e.g. PGPoolingDataSource.
     *
     * @param config Configuration object with properties for creating DataSource.
     * @return DataSource for connecting to PostgreSQL database.
     */
    protected DataSource createDataSource(final PostgresConfig config) {
        new PGSimpleDataSource(
                user: config.user,
                password: config.password,
                serverName: config.serverName,
                databaseName: config.databaseName,
                portNumber: config.portNumber)
    }
    
}

Let's see how we can add this module to our Ratpack application. We use the Groovy DSL for our application. The following snippet of the bindings block uses the module method to add our PostgresModule:

// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.postgres.PostgresConfig
import com.mrhaki.ratpack.postgres.PostgresModule
import ratpack.config.ConfigData
import ratpack.config.ConfigDataBuilder
import ratpack.groovy.sql.SqlModule

import static ratpack.groovy.Groovy.ratpack

ratpack {

    bindings {
        // Create generic configuration.
        final ConfigData configData = ConfigData.of { ConfigDataBuilder builder ->
            // Set configuration properties.
            // We can use the yaml, json and other
            // ConfigDataBuilder methods to read
            // configuration from other sources.
            builder.props(
                    ['postgres.user'        : 'postgres',
                     'postgres.password'    : 'secret',
                     'postgres.portNumber'  : 5432,
                     'postgres.databaseName': 'postgres',
                     'postgres.serverName'  : '192.168.99.100'])
            builder.build()
        }

        // Create instance of PostgresConfig 
        // that is used for the 
        // configurable module PostgresModule.
        bindInstance PostgresConfig, configData.get('/postgres', PostgresConfig)
        // Initialise module to create DataSource.
        module PostgresModule

        // Initialize SqlModule to provide
        // Groovy SQL support in our application.
        module SqlModule
    }

    ...
}

The DataSource we created is very simple and doesn't support connection pooling. Ratpack has a framework module to add connection pooling support from the Hikari library to our application. Hikari is a solid, high performance JDBC connection pooling framework. To add it to our application we must add a dependency to our Gradle build file and add the module in our bindings block.

// File: build.gradle
...
dependencies {
    ...
    // Use Ratpack extension for including
    // dependency to Hikari with correct
    // version for the Ratpack version we use.
    compile ratpack.dependency('hikari')
    ...
}
...

We re-use our PostgresModule class to set the dataSource property of the Hikara configuration:

// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.postgres.PostgresConfig
import com.mrhaki.ratpack.postgres.PostgresModule
import com.zaxxer.hikari.HikariConfig
import ratpack.config.ConfigData
import ratpack.config.ConfigDataBuilder
import ratpack.groovy.sql.SqlModule
import ratpack.hikari.HikariModule

import static ratpack.groovy.Groovy.ratpack

ratpack {

    bindings {
        // Create generic configuration.
        final ConfigData configData = ConfigData.of { ConfigDataBuilder builder ->
            // Set configuration properties.
            // We can use the yaml, json and other
            // ConfigDataBuilder methods to read
            // configuration from other sources.
            builder.props(
                    ['postgres.user'        : 'postgres',
                     'postgres.password'    : 'secret',
                     'postgres.portNumber'  : 5432,
                     'postgres.databaseName': 'postgres',
                     'postgres.serverName'  : '192.168.99.100'])
            builder.build()
        }

        // Configure HikariModule. There are different
        // ways to set properties. Here we assign
        // a DataSource instance to the dataSource property.
        module HikariModule, { HikariConfig config ->
            config.dataSource =
                    new PostgresModule().dataSource(
                            configData.get('/postgres', PostgresConfig))
        }

        // Initialize SqlModule to provide
        // Groovy SQL support in our application.
        module SqlModule
    }

    ...
}

Written with Ratpack 1.1.1.