Show Navigation

Build a Spring Boot application with GORM

Learn how to build a Spring Boot application using GORM

Authors: Ben Rhine, Sergio del Amo

Grails Version: N/A - Spring Boot Version: 2.2.2.RELEASE

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 Spring Boot 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.8 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-without-grails/initial

and follow the instructions in the next sections.

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

If you want to start from scratch, create a new Spring Boot application using Spring Initializr.

Select a Gradle Project using Groovy with Spring Boot 1.5.6. Once you have selected your build and project type, set the Group to your organization ie. com.example in our case we will use demo.

Once that is done add the Web and h2 dependencies to the project.

buildAndMetadata

Click Generate Project.

3 Configuring your Build

Edit build.gradle files to include GORM dependencies.

/build.gradle
plugins {
        id 'org.springframework.boot' version '2.2.2.RELEASE'
        id 'io.spring.dependency-management' version '1.0.8.RELEASE'
        id 'groovy'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
        mavenCentral()
        maven { url "https://repo.grails.org/grails/core" } (1)
}

dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.codehaus.groovy:groovy'

        (2)
        implementation("org.grails:gorm-hibernate5-spring-boot:7.0.1.RELEASE")
        implementation "org.hibernate:hibernate-core"
        implementation "org.hibernate:hibernate-ehcache"

        (3)
        runtime "org.apache.tomcat:tomcat-jdbc:8.5.0"
        runtime "org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.5.0"

        runtimeOnly 'com.h2database:h2'
        testImplementation('org.springframework.boot:spring-boot-starter-test') {
                exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
        }

        testImplementation "org.springframework.boot:spring-boot-starter-webflux" (4)
}

test {
        useJUnitPlatform()
}
1 Repository to resolve GORM dependencies.
2 Add the required dependencies for GORM to our project.
3 For connection pooling
4 To use WebClient in the tests, include the spring-webflux module in your project.

3.1 Configuration

GORM for Hibernate can be configured with src/main/resources/application.yml file when using Spring Boot.

src/main/resources/application.yml
hibernate:
    hbm2ddl:
        auto: update
    dialect:  org.hibernate.dialect.H2Dialect
spring:
    datasource:
        driverClassName: org.h2.Driver
        url: jdbc:h2:mem:devDb
        username: sa
        password: ""

4 Writing the Application

We are going to create a package structure similar to the one you find in Grails application:

  • demo.controller

  • demo.domain

  • demo.service

  • demo.init

4.1 Creating the domain

Now with our packages in place lets go ahead and create our 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/domain/
$ 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/domain/Manufacturer.groovy
package demo.domain

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

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.

4.2 Seed Data

When the application starts we save some data.

/src/main/groovy/demo/Application.groovy
package demo

import org.springframework.boot.ApplicationArguments
import demo.init.BootStrap
import groovy.transform.CompileStatic
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.ApplicationRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

@CompileStatic
@SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class) (1)
class Application implements ApplicationRunner {

    @Autowired
    BootStrap bootStrap

    static void main(String[] args) {
        SpringApplication.run Application, args
    }

    void run(ApplicationArguments args) throws Exception {
        bootStrap.init()
    }
}
1 Disable Auto-configuration for Hibernate JPA.
/src/main/groovy/demo/init/BootStrap.groovy
package demo.init

import demo.domain.Manufacturer
import demo.domain.Vehicle
import demo.service.ManufacturerService
import grails.gorm.transactions.Transactional
import groovy.transform.CompileStatic
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component

@Component
@CompileStatic
class BootStrap {

    @Autowired
    ManufacturerService manufacturerService

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

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

4.3 Creating the Service layer

Next lets create our service layer for our application.

  $ cd src/main/groovy/demo/service
  $ touch ManufacturerService
  $ touch VehicleService

We are going to use GORM Data Services.

Data Services take the work out of implemented service layer logic by adding the ability to automatically implement abstract classes or interfaces using GORM logic.

/src/main/groovy/demo/service/ManufacturerService.groovy
package demo.service

import demo.domain.Manufacturer
import grails.gorm.transactions.ReadOnly
import grails.gorm.transactions.Transactional
import groovy.transform.CompileStatic
import org.springframework.stereotype.Service

@CompileStatic
@grails.gorm.services.Service(Manufacturer)
@Service
interface ManufacturerService {

    List<Manufacturer> findAll()

    Manufacturer save(Manufacturer manufacturer)
}
/src/main/groovy/demo/service/VehicleService.groovy
package demo.service

import demo.domain.Manufacturer
import demo.domain.Vehicle
import grails.gorm.services.Where
import groovy.transform.CompileStatic
import org.springframework.stereotype.Service

@CompileStatic
@grails.gorm.services.Service(Vehicle)
@Service
interface VehicleService {

    @Where({ manufacturer.name == manufacturerName })
    List<Vehicle> findAllByManufacturer(String manufacturerName)

}

4.4 Creating a controller

Finally lets create some controllers so we can have basic UI access to our data.

  $ cd src/main/groovy/demo/controller
  $ touch ManufacturerController
  $ touch VehicleController

Now let’s edit our controllers under src/main/groovy/demo/controller.

The controllers will have the following annotations

  • @RestController - Denotes the controller as restful and that it can return data via url

  • @Autowired - Allows use to use dependency injection to access our services

  • @RequestMapping("/") - Sets the url mapping for the method

/src/main/groovy/demo/controller/VehicleController.groovy
package demo.controller

import demo.service.VehicleService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class VehicleController {

    @Autowired VehicleService vehicleService

    @RequestMapping("/{manufacturerName}/vehicles")
    List<String> vehiclesByManufacturer(@PathVariable String manufacturerName) {
        vehicleService.findAllByManufacturer(manufacturerName)*.name
    }
}

Add a test:

/src/test/groovy/demo/controller/VehicleControllerTest.groovy
package demo.controller

import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class VehicleControllerTest {

    @Autowired
    WebTestClient webClient

    @Test
    void fetchAudiVehicles() {
        this.webClient.get().uri("/audi/vehicles").exchange().expectBody(String).isEqualTo('["A3","A4"]')
    }
}
/src/main/groovy/demo/controller/ManufacturerController.groovy
package demo.controller

import demo.service.ManufacturerService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class ManufacturerController {

    @Autowired
    ManufacturerService manufacturerService

    @RequestMapping("/")
    List<String> index(){
        manufacturerService.findAll()*.name
    }
}

Add a test:

/src/test/groovy/demo/controller/ManufacturerControllerTest.groovy
package demo.controller

import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.reactive.server.WebTestClient

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ManufacturerControllerTest {

    @Autowired
    WebTestClient webClient

    @Test
    void fetchManufacturer() {
        this.webClient.get().uri("/").exchange().expectBody(String).isEqualTo('["audi","ford"]')
    }
}

Run the app:

./gradlew bootRun

You should be able to call the endpoints:

curl "http://localhost:8080"

and get the response:

["audi","ford"]

Or retrieve the vehicles of a manufacturer:

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

and get the response:

["A3","A4"]

5 Do you need help with GORM or Grails?

Object Computing, Inc. (OCI) sponsored the creation of this Guide. A variety of consulting and support services are available.

OCI is Home to Grails

Meet the Team