Spring Boot + Groovy: From Zero to Hero
Tags: spring, groovy, jpa, flywaydb
Published: 2019-01-10 17:17:01

---
title: Spring Boot + Groovy: From Zero to Hero
published: true
description: How to create an API using Spring Boot + Groovy from scratch.
tags: spring,groovy,jpa,flywaydb
cover_image: https://thepracticaldev.s3.amazonaws.com/i/kqcla6ngdh5sela8ze3b.jpg
---

Java... ugghh...

![](https://media.giphy.com/media/54PaD9dWT0go/giphy.gif)

I don't like to use Java (and please don't use it) but sometimes I have to... and that was the case.

![](https://media.giphy.com/media/eBCnpuRGBhQGY/giphy-downsized-large.gif)

A few weeks ago I started a new project at [Indigo](https://www.dsindigo.com/) (please follow them!) and the chosen framework to do the job was [Spring Boot](http://spring.io/projects/spring-boot) and this time I had no option since the other person who is developing with me loves the combination **Java + Spring Boot**.

![](https://media.giphy.com/media/Pch8FiF08bc1G/giphy.gif)

But why do I said "the combination"? 🤔 Well, there are some programming languages that work on the JVM such as Kotlin, Scala and my favorite [Groovy](http://groovy-lang.org/)!.

## What is Groovy?

According to it's official page:

> Apache Groovy is a powerful, optionally typed and dynamic language, with static-typing and static compilation capabilities, for the Java platform aimed at improving developer productivity thanks to a concise, familiar and easy to learn syntax. It integrates smoothly with any Java program, and immediately delivers to your application powerful features, including scripting capabilities, Domain-Specific Language authoring, runtime and compile-time meta-programming and functional programming.

In other words, you can do better stuff, write less code and get some cool features that Java cannot.

## What will be our example?

This time we will create a Pokemon API!! Let's catch'em all

![](https://media.giphy.com/media/W04QVzelTHsNW/giphy.gif)

Our API must:
* Get the information of all trainers
* Get the information of a specific trainer
* Get all caught pokemon of a specific trainer

## What will be using to solve that?

* Java 8
* Gradle
* Spring Boot
* Groovy
* MySQL
* Flyway DB

## What do I need to install?

* Java 8 or greater
* Gradle
* MySQL

I highly recommend you to install Java and Gradle via [SDKMAN](https://sdkman.io).

Let's get started!

## 1. Create the base project with Gradle

Create a directory and then create a file named `build.gradle`. This file will:

* Download all the dependencies
* Compile the entire project
* Run Spring Boot server
* Run tests if needed

So, let's add the following lines:


```groovy
buildscript {
  ext {
    springBootVersion = '2.0.6.RELEASE'
  }
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
  }
}

apply plugin: 'groovy'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

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

repositories {
  mavenCentral()
}

dependencies {
  implementation("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
  implementation('org.codehaus.groovy:groovy:2.5.3')
}

```

The lines tell Gradle to configure Maven Central as the default repository, download and use the `spring-boot-gradle-plugin` and download the following dependencies:

* **Spring Boot Web**: Exposes REST services
* **Groovy**: Compiles and interprets all Groovy/Java code

Also our base package name will be `com.pokemon` so every class with this package name will be compiled by Gradle and used by Spring Boot.

## 2. Create the base code and run it for the first time

Create the directories `src/main/groovy/com/pokemon` in the root directory and then create a file named `Application.groovy`:

```groovy
package com.pokemon

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

// In groovy every class is public by default
// so you don't need to write "public class" anymore
@SpringBootApplication
class Application {

    // Every method is public as well
    static void main(String[] args) {
        
        // You can omit the last method call parenthesis
        // This is the same as .run(Application, args)
        // also you can omit ;
        SpringApplication.run Application, args
    }
}

```

Open your terminal, go to your project directory and type the command `gradle bootRun` to get your application running like this: 

![](https://media.giphy.com/media/8gKOQx8DDF6BkRRNhL/giphy.gif)

## 3. SQL tables with Flyway DB

Working with SQL databases is often a pain. 

How do you share the database structure with your team? 

A way to deal with it is create a SQL script and share it with all the developers.

Sounds good, right?

![](https://media.giphy.com/media/111ebonMs90YLu/giphy.gif)

But, when you need to add changes (like modifying existing tables or adding new ones) to the script, What do you do now? How do the dev team get notified from those changes?

Framework such as PHP Laravel introduce the _migration_ concept. Every change to the database (creating a table, adding a field, changing a field type, etc) is a _migration_. Migrations are executed in certain order and keep the database always up-to-date.

**Flyway DB** will help us to solve the problem but in Java (Groovy).

According to it's official page:

> Version control for your database.
Robust schema evolution across all your environments.
With ease, pleasure and plain SQL.

So now let's add **Flyway DB**, **Spring Data** and **MySQL Connector** to our project.

Edit `build.gradle` file and the following dependencies:

```groovy
implementation('org.flywaydb:flyway-core:5.2.0')
implementation("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
implementation('mysql:mysql-connector-java:8.0.13')
```

And now create a new file in `src/main/resources` directory named `application.properties`:

```properties
spring.flyway.url=jdbc:mysql://localhost/
spring.flyway.user=your-username
spring.flyway.password=your-password
spring.flyway.schemas=your-database-name

spring.datasource.url=jdbc:mysql://localhost/your-database-name
spring.datasource.username=your-username
spring.datasource.password=your-password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
```

Don't forget to change **your-username**, **your-password** and **your-database-name** values.

In my case, the database name is `pokemon_example`.

**spring.flyway** credentials let **FlywayDB** connect to the database and install the lastest changes every time you run the project.

**spring.datasource** credentials let **Spring Data** connect to the database and run queries.

To create the initial tables create a new file in `src/main/resources/db/migration` directory named `V1__creating_initial_tables.sql`:

```sql
CREATE TABLE trainers(
  id INTEGER AUTO_INCREMENT,
  name VARCHAR(100) NOT NULL,
  level SMALLINT NOT NULL DEFAULT 1,
  PRIMARY KEY (id)
);

CREATE TABLE pokemon(
  id INTEGER AUTO_INCREMENT,
  name VARCHAR(20) NOT NULL,
  number SMALLINT NOT NULL,
  PRIMARY KEY (id)
);

CREATE TABLE wild_pokemon(
  id INTEGER AUTO_INCREMENT,
  combat_power SMALLINT NOT NULL DEFAULT 0,
  pokemon_id INTEGER NOT NULL,
  trainer_id INTEGER,
  PRIMARY KEY (id),
  FOREIGN KEY (pokemon_id) REFERENCES pokemon (id)
    ON DELETE CASCADE,
  FOREIGN KEY (trainer_id) REFERENCES trainers (id)
    ON DELETE SET NULL
);
```

**Flyway DB** search for all the files in `db/migration` directory with the pattern **VN__any_name.sql** where N is a unique version, for example:

* 1
* 001
* 5.2
* 1.2.3.4.5.6.7.8.9
* 205.68
* 20130115113556
* 2013.1.15.11.35.56
* 2013.01.15.11.35.56

Personally I prefer to use this versioning:

* V1__whatever.sql
* V2__whatever.sql
* etc.

For last, run the project with `gradle bootRun` command and then take a look to your database.

![](https://media.giphy.com/media/etn4pXJcDkR4DDYHiy/giphy.gif)

## 4. Entity classes (Domain)

Back to code, after creating the database successfully it's time to map those tables to classes.

In `src/main/groovy/com/pokemon` directory, create a new one named `entity` and create 3 new files:

* `Trainer.groovy`
* `Pokemon.groovy`
* `WildPokemon.groovy`

_Trainer.groovy_
```groovy
package com.pokemon.entity

import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.GeneratedValue
import javax.persistence.Table
import javax.persistence.GenerationType
import javax.validation.constraints.NotNull
import javax.persistence.Column

@Entity
@Table(name = "trainers")
class Trainer {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  Integer id

  @NotNull
  @Column(nullable = false)
  String name

  @NotNull
  @Column(nullable = false)
  Short level
}
```

_Pokemon.groovy_
```groovy
package com.pokemon.entity

import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.GeneratedValue
import javax.persistence.Table
import javax.persistence.GenerationType
import javax.validation.constraints.NotNull
import javax.persistence.Column

@Entity
@Table(name = "pokemon")
class Pokemon {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  Integer id

  @NotNull
  @Column(nullable = false)
  String name

  @NotNull
  @Column(nullable = false)
  Short number
}
```

_WildPokemon.groovy_
```groovy
package com.pokemon.entity

import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.GeneratedValue
import javax.persistence.Table
import javax.persistence.GenerationType
import javax.validation.constraints.NotNull
import javax.persistence.Column
import javax.persistence.ManyToOne
import javax.persistence.JoinColumn

@Entity
@Table(name = "wild_pokemon")
class WildPokemon {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  Integer id

  @NotNull
  @Column(name = "combat_power", nullable = false)
  Integer combatPower

  @ManyToOne
  @JoinColumn(name = "pokemon_id", referencedColumnName = "id", nullable = false)
  Pokemon pokemon

  @ManyToOne
  @JoinColumn(name = "trainer_id", referencedColumnName = "id", nullable = true)
  Trainer trainer
}
```
Before continue, remember!! because we are using Groovy there's no need to add the boring _getters_ and _setters_:

```groovy
// Groovy version
class Example {

  String property
}

Example ex = new Example(property: "hello world")
ex.property = "hola inmundo"

// -----------------------------------------------------

// Java version
public class Example {

  private String property;

  public Example(String property) {
    this.property = property;
  }

  public String getProperty() {
    return property;
  }

  public void setProperty(String property) {
    this.property = property;
  }
}

Example ex = new Example("hello world")
ex.setProperty("hola inmundo")
```

Writing less code FTW! 🤙

As you can see, it's easy to create entity classes based on tables. In the `WildPokemon` case we declared 2 many to one relationships, one to `Pokemon` class (or table) and the other one to `Trainer` class (or table). Only `Trainer` relationship can be null.

## 5. Repository classes (Persistence layer)

A `Repository` class is the place to define our queries. It's very easy and magical at the same time.

Remember our API goal:
* Get the information of all trainers
* Get the information of a specific trainer
* Get all caught pokemon of a specific trainer

Okay, let's translate those features into SQL queries:

```sql
-- Get the information of all trainers
SELECT * FROM trainers;

-- Get the information of a specific trainer
SELECT * FROM trainers WHERE id = ?;

-- Get all caught pokemon of a specific trainer
SELECT * FROM wild_pokemon WHERE trainer_id = ?;
```

Seems legit.

But what if the queries were written in human readable text?

```bash
# Get the information of all trainers
find all trainers

# Get the information of a specific trainer
find trainer by id

# Get all caught pokemon of a specific trainer
find wild pokemon by trainer id
```

And believe or not, using `JpaRepository` interface from **Spring Data** our queries are:

* `findAll`
* `findById`
* `findByTrainerId`

Very easy!! 🎉🎉🎉🎉🎉

Let's code them, in `src/main/groovy/com/pokemon` directory create a new one named `repository` and create 2 new files:

* `TrainerRepository.groovy`
* `WildPokemonRepository.groovy`

_TrainerRepository.groovy_
```groovy
package com.pokemon.repository

import com.pokemon.entity.Trainer
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface TrainerRepository extends JpaRepository<Trainer, Integer> {

  List<Trainer> findAll()

  Trainer findById(Integer id)
}
```

_WildPokemonRepository.groovy_
```groovy
package com.pokemon.repository

import com.pokemon.entity.WildPokemon
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface WildPokemonRepository extends JpaRepository<WildPokemon, Integer> {

  List<WildPokemon> findByTrainerId(Integer trainerId)
}
```

Simple as that, **Spring Data** will search for all the classes annotated with `@Repository` and verify the method signature.

## 6. Service classes (Business layer)

Our API doesn't need business logic at all but still is a good idea to create service classes and use repositories in them.

In `src/main/groovy/com/pokemon` directory create a new one named `service` and then inside `service` create a new one named `impl`.

Our `service` directory will contain interfaces and `impl` their implementation.

Wait, what?

![](https://media.giphy.com/media/oOxBQwNqGwxeWLDF6A/giphy.gif)

It has its explanation.

Suppose you have to implement a class that stores files:

```groovy
class FileStorage {

  String saveFile(String base64) {
    // storing file in disk...
  }
}
```

Pretty simple, but then your client needs to store the files in Amazon S3 and Azure Blob too. What is the easiest way to solve the problem?

You can create a interface, because in-disk storage, Amazon S3 storage and Azure Blob storage do the same thing, save a file:

```groovy
interface Storage {

  String saveFile(String base64)
}
```

And then you implement the class for each storage type.

```groovy
class S3Storage implements Storage {
}

class BlobStorage implements Storage {
}

class DiskStorage implements Storage {
}
```

And finally you can use `polymorphism` to change the storage type whenever you need it: 

```groovy
// Save the file in Amazon S3
Storage storage = new S3Storage()

// Save the file in Azure Blob
Storage storage = new BlobStorage()

// Save the file in disk
Storage storage = new DiskStorage()
```

That's why we create interfaces, because the business logic can change anytime or whenever the client wants to.

So, let's create our 2 interfaces and their implementation:

_TrainerService.groovy_
```groovy
package com.pokemon.service

import com.pokemon.entity.Trainer

interface TrainerService {

  List<Trainer> findAll()

  Trainer findById(int id)
}
```

_TrainerServiceImpl.groovy_
```groovy
package com.pokemon.service.impl

import com.pokemon.entity.Trainer
import com.pokemon.service.TrainerService
import com.pokemon.repository.TrainerRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class TrainerServiceImpl implements TrainerService {

  @Autowired
  private final TrainerRepository trainerRepository

  @Override
  List<Trainer> findAll() {
    trainerRepository.findAll()
  }

  @Override
  Trainer findById(int id) {
    trainerRepository.findById(id)
  }
}
```

`WildPokemonService` implementation is very similar.

Our implementation class has `@Service` annotation because **Spring** will create an instance for us when another class uses `@Autowired` annotation.

In `TrainerServiceImpl` we use `@Autowired` annotation to create an instance of `TrainerRepository` class.

Annotations are very important because they tell **Spring** what they are and what to do with them.

## 7. Controller classes

Finally it's time to create the controller with 3 endpoints:

* `/trainers` - Get all trainers
* `/trainers/{ id }` - Get a specific trainer
* `/trainers/{ id }/pokemon` - Get all caught pokemon of a specific trainer

In `src/main/groovy/com/pokemon` directory create a new one named `controller` and then create a new file named `TrainerController.groovy`:

```groovy
package com.pokemon.controller

import com.pokemon.entity.Trainer
import com.pokemon.entity.WildPokemon
import com.pokemon.service.TrainerService
import com.pokemon.service.WildPokemonService
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.PathVariable

@RestController
@RequestMapping('/trainers')
class TrainerController {

  @Autowired
  private final TrainerService trainerService

  @Autowired
  private final WildPokemonService wildPokemonService

  @RequestMapping(method = RequestMethod.GET)
  List<Trainer> findAll() {
    trainerService.findAll()
  }

  @RequestMapping(value = '/{id}/pokemon', method = RequestMethod.GET)
  List<Trainer> findCaughtPokemon(@PathVariable('id') int id) {
    wildPokemonService.findByTrainer id
  }

  @RequestMapping(value = '/{id}', method = RequestMethod.GET)
  Trainer findById(@PathVariable('id') int id) {
    trainerService.findById id
  }
}
```
`@RequestMapping` annotation configures the prefix of all endpoints defined in that controller. Also configures method type (GET, POST, PUT, DELETE, etc).

`@Autowired` annotation creates instances of service classes for us.

`@PathVariable` annotation passes the specified URL parameter to the method call.

For last, let's insert dummy data into MySQL using **Flyway DB**. Create a new migration (in `src/main/resources/db/migration`) named `V2__inserting_example_data.sql`:

```sql
INSERT INTO trainers VALUES (1, 'Red', 40), (2, 'Ash Ketchum', 10);

INSERT INTO pokemon VALUES
  (1, 'Bulbasaur', 1), (2, 'Ivysaur', 2), (3, 'Venosaur', 3), (4, 'Charmander', 4),
  (5, 'Charmeleon', 5), (6, 'Charizard', 6), (7, 'Squirtle', 7), (8, 'Wartortle', 8),
  (9, 'Blastoise', 9);

INSERT INTO wild_pokemon VALUES
  (1, 2000, 1, 3), (2, 2100, 4, 6), (7, 2000, 7, 9), (8, 600, 1, 2);
```

Run the project with `gradle bootRun` command, go to your favorite browser and navigate to [http://localhost:8080/trainers/1/pokemon](http://localhost:8080/trainers/1/pokemon) to get:

```json
[
   {
      "id":1,
      "combatPower":2000,
      "pokemon":{
         "id":3,
         "name":"Venosaur",
         "number":3
      },
      "trainer":{
         "id":1,
         "name":"Red",
         "level":40
      }
   },
   {
      "id":2,
      "combatPower":2100,
      "pokemon":{
         "id":6,
         "name":"Charizard",
         "number":6
      },
      "trainer":{
         "id":1,
         "name":"Red",
         "level":40
      }
   },
   {
      "id":7,
      "combatPower":2000,
      "pokemon":{
         "id":9,
         "name":"Blastoise",
         "number":9
      },
      "trainer":{
         "id":1,
         "name":"Red",
         "level":40
      }
   }
]
```

This is our final project directory structure:

![](https://thepracticaldev.s3.amazonaws.com/i/pwc9ipwwndkgl3baohna.png)

And that's all, folks!

Thank you so much for reading this long post.

In the next post we will improve our pokemon API adding more features and things like Data Transfer Objects, Exception Handler, Swagger, Testing and more! 

Stay tuned.

{% github jorgeramon/spring-groovy-pokemon %}