Show Navigation

Build a Ratpack application which uses GORM

Learn how to build a Ratpack application which uses GORM as data access toolkit

Authors: Sergio del Amo

Grails Version: 3.3.1

1 Grails Training

Grails Training - Developed and delivered by the folks who created and actively maintain the Grails framework!.

2 Getting Started

In this guide, you are going to build a Ratpack application using GORM.

2.1 What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 1.7 or greater installed with JAVA_HOME configured appropriately

2.2 How to complete the guide

To get started do the following:

or

The Grails guides repositories contain two folders:

  • initial Initial project. Often a simple Grails app with some additional code to give you a head-start.

  • complete A completed example. It is the result of working through the steps presented by the guide and applying those changes to the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/gorm-ratpack/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/gorm-ratpack/complete

3 Writing the Application

3.1 Configuring your Build

Edit build.gradle file to include GORM dependencies.

build.gradle
dependencies {
    compile "org.grails:grails-datastore-gorm-hibernate5:6.1.6.RELEASE"
    runtime "com.h2database:h2:1.4.192"
    runtime "org.apache.tomcat:tomcat-jdbc:8.5.0"
    runtime "org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.5.0"
    runtime "org.slf4j:slf4j-api:1.7.10"
}

For this guide, use H2; an in-memory database.

Read GORM documentation to learn more.

3.2 GORM Configuration

The Ratpack app developed during this guide uses Google Guice. Google Guice provides the concept of a module, which is a kind of recipe for providing object.

Create a module to configure GORM.

src/main/groovy/demo/GormModule.groovy
package demo

import com.google.inject.AbstractModule
import com.google.inject.Provides
import groovy.transform.CompileStatic
import org.grails.orm.hibernate.HibernateDatastore

@CompileStatic
class GormModule extends AbstractModule {

    @Override
    protected void configure() {}

    @Provides
    HibernateDatastore hibernateDatastore() {
        Map<String, Object> configuration = [
            'hibernate.hbm2ddl.auto':'create-drop',
            'dataSource.url':'jdbc:h2:mem:myDB'
        ] as Map<String, Object>

        new HibernateDatastore(configuration, getClass().getPackage())
    }
}

GORM for Hibernate can be configured by passing a Map or instanceof the PropertyResolver interface to the org.grails.orm.hibernate.HibernateDatastore class when used standalone.

3.3 Creating the domain

Create two domain objects.

  • Manufacturer.groovy

  • Vehicle.groovy

Feel free to use your favorite IDE to create these or execute the following

$ cd complete/src/main/groovy/demo
$ touch Manufacturer.groovy
$ touch Vehicle.groovy

Now that all our class stubs are in place lets go ahead and edit them.

src/main/groovy/demo/Manufacturer.groovy
package demo

import grails.gorm.annotation.Entity
import groovy.transform.ToString
import org.grails.datastore.gorm.GormEntity

@ToString
@Entity
class Manufacturer implements GormEntity<Manufacturer> {

    String name

    static hasMany = [vehicles: Vehicle]

    static constraints = {
        name blank: false
    }
}
src/main/groovy/demo/domain/Vehicle.groovy
package demo

import grails.gorm.annotation.Entity
import org.grails.datastore.gorm.GormEntity
import groovy.transform.ToString

@ToString
@Entity
class Vehicle implements GormEntity<Vehicle> {
    String name
    Integer year
    static belongsTo = [manufacturer: Manufacturer]

    static constraints = {
        name nullable: false, blank: false
    }
}

Manufacturer and Vehicle have a one-to-many relationship.

We are using GORM outside of Grails. Because of that, we need to annotate our domain classes with the grails.gorm.annotation.Entity. Additionally we implement the GormEntity trait. It is merely to aid IDE support of GORM outside of Grails.

3.4 Seed Data

Create a Service to encapsulate the creation of sample data when the application starts.

/src/main/groovy/demo/BootStrapService.groovy
package demo

import grails.gorm.transactions.Transactional
import groovy.transform.CompileStatic
import org.grails.orm.hibernate.HibernateDatastore
import ratpack.exec.Blocking
import ratpack.service.Service
import ratpack.service.StartEvent

@CompileStatic
class BootStrapService implements Service {
    void onStart(StartEvent e) throws Exception {
        e.getRegistry().get(HibernateDatastore)
        Blocking.exec {
            populateWithSampleData()
        }
    }

    @Transactional
    void populateWithSampleData() {
        Manufacturer audi = new Manufacturer(name: 'audi')
        audi.addToVehicles(new Vehicle(name: 'A3', year: 1996))
        audi.addToVehicles(new Vehicle(name: 'A4', year: 1994))
        audi.save()

        Manufacturer ford = new Manufacturer(name: 'ford')
        ford.addToVehicles(new Vehicle(name: 'Ford KA', year: 1996))
        ford.save()
    }
}

3.5 Creating a Handler

Create a Handler. We handle two requests.

Requests to / return a list of manufacturers. Requests to /audi/vehicles returns a list of vehicles of the manufacturer named audi

src/main/groovy/demo/ManufacturerHandler.groovy
package demo

import grails.gorm.transactions.ReadOnly
import groovy.transform.CompileStatic
import ratpack.exec.Blocking
import ratpack.groovy.handling.GroovyContext
import ratpack.groovy.handling.GroovyHandler
import static ratpack.jackson.Jackson.json

@CompileStatic
class ManufacturerHandler extends GroovyHandler {
    @Override
    protected void handle(GroovyContext context) {
        String manufacturerName = context.pathTokens.id
        Blocking.get {
            manufacturerName ? findAllVehicleNameByManufacturerName(manufacturerName) : findAllManufacturerName()
        } then { names ->
            context.render(json(names))
        }
    }

    @ReadOnly
    List<String> findAllVehicleNameByManufacturerName(String manufacturerName) {
        Vehicle.where { manufacturer.name == manufacturerName }.projections {
            property('name')
        }.list() as List<String>
    }
    @ReadOnly
    List<String> findAllManufacturerName() {
        Manufacturer.where {}.projections {
            property('name')
        }.list() as List<String>
    }
}

3.6 Ratpack.groovy

Replace the content of src/ratpack/Ratpack.groovy.

src/ratpack/Ratpack.groovy
import demo.BootStrapService
import demo.GormModule
import demo.ManufacturerHandler

import static ratpack.groovy.Groovy.ratpack

ratpack {
  bindings {
    module GormModule
    bindInstance new BootStrapService()
    add(new ManufacturerHandler())
  }
  handlers {
    get(":id/vehicles", ManufacturerHandler)
    get(ManufacturerHandler)
  }
}

The previous code registers the Module which configures GORM, the Service which populates the database on start-up and the Handler.

Run the app:

./gradlew run

You should be able to call the endpoints:

curl "http://localhost:5050"

and get the response:

["audi","ford"]

Or retrieve the vehicles of a manufacturer:

curl "http://localhost:5050/audi/vehicles"

and get the response:

["A3","A4"]

4 Do you need help with GORM or Grails?

OCI sponsored the creation of this Guide. OCI offers several Grails services:

Free consultation

The OCI Grails Team includes Grails co-founders, Jeff Scott Brown and Graeme Rocher. Check our Grails courses and learn from the engineers who developed, matured and maintain Grails.

Grails OCI Team