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: 4

1 Grails Training

Apache Grails Training

Apache Grails is now part of the Apache Software Foundation. The community-maintained training catalog is being migrated; in the meantime see the Learning page for current resources, recorded talks, and links to other community-supplied training material.

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 11 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.

The initial contains a Ratpack app.

build.gradle
// missing snippet: ../initial/build.gradle

with a GET / endpoint returning hello world.

src/ratpack/Ratpack.groovy
// missing snippet: ../initial/src/ratpack/Ratpack.groovy
  • 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

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 static ratpack.groovy.Groovy.ratpack

ratpack {
  handlers {
    get {
        render "Hello World"
    }
  }
}

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?

Help with Apache Grails

Apache Grails is supported by an active community of contributors and the Apache Software Foundation. If you need help working through a guide, want to discuss the framework, or have run into something that looks like a bug, the channels below are the right place to start.

For Grails plugins, see the matching project on the apache org or the plugin’s own GitHub repository.