Spring Boot Groovy PostgreSQL CRUD REST API

by Didin J. on Mar 13, 2022 Spring Boot Groovy PostgreSQL CRUD REST API

The comprehensive step by step tutorial on building CRUD REST API using Spring Boot, Groovy, and PostgreSQL

In the previous tutorial, we have shown you how to create REST API easily in Groovy language using Grails Framework which everything is set up in Grails ways. Now, we will do the same thing but create with Spring Boot ways like configure sever and database, create an entity, create a repository, create service, and create REST controller. Using Groovy will decrease the codes lines instead of using Java. So, CRUD (Create, Read, Update, Delete) operation to build REST API simple and less library to use. 

This tutorial is divided into several steps:

Before we start, the following tools, framework, library, and dependencies are required:

  1. JDK 11
  2. Maven
  3. Spring Boot
  4. Spring Data PostgreSQL
  5. Spring Initializr
  6. IDE (Netbeans, Eclipse, Spring Tool Suite, or IntellijIdea)

You can watch the video tutorial on our YouTube channel here.

Let's get started with the main steps!


Step #1: Create and Configure Spring Boot Groovy Project

We assume that you have installed JDK 11, Gradle, and IDE (Netbeans, Eclipse, STS, or IntellijIdea). Next, we will create a new Spring Boot Groovy Gradle project of REST API using Spring Initializer. Spring Initializr provides an extensible API to generate quickstart projects, and to inspect the metadata used to generate projects, for instance, to list the available dependencies and versions. Just go to Spring Initializr web-based Spring project generator then fill the required frameworks and libraries.

spring-initializr

Fill the required fields as above, also, add the standard dependencies for creating REST APIs such as Spring Web, PostgreSQL Driver, and Spring Data JPA. Click the Generate button to download the compressed Spring Boot project. Next, extract the downloaded Spring Boot project then open it with your IDE (we are using IntellijIdea).

Next, open src/main/resources/application.properties then add these lines of server port, path, JPA, and PostgreSQL connection configuration.

server.port=8080
server.servlet.context-path=/api/v1
spring.jpa.database=POSTGRESQL
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=create
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://localhost:5432/groovyrest
spring.datasource.username=djamware
spring.datasource.password=dj@mw@r3
spring.datasource.driver-class-name=org.postgresql.Driver

As you see, we need to create a new PostgreSQL database. For that, run the PostgreSQL console in the terminal.

psql postgres --u postgres

Next, type this command for creating a new user with a password then give access for creating the database.

postgres-# CREATE ROLE djamware WITH LOGIN PASSWORD 'dj@mw@r3';
postgres-# ALTER ROLE djamware CREATEDB;

Quit `psql` then log in again using the new user that was previously created.

postgres-# \q
psql postgres -U djamware

Enter the password, then you will enter this `psql` console.

psql (9.5.13)
Type "help" for help.

postgres=>

Type this command to create a new database.

postgres=> CREATE DATABASE groovyrest;

Then give that new user privileges to the new database then quit the `psql`.

postgres=> GRANT ALL PRIVILEGES ON DATABASE groovyrest TO djamware;
postgres=> \q


Step #2: Create Groovy Model or Entity Class

In this tutorial, we will use 2 entity with one to many relationship. There's a Product entity which has many Variant entity as describe in this diagram.

product-variant-relationship

To create that entity-relationship, first, we need to create a new package with the name com/djamware/groovyrest/entity the new Groovy files (com/djamware/groovyrest/entity/Product.groovy and com/djamware/groovyrest/entity/Variant.groovy). Next, open and edit com/djamware/groovyrest/entity/Product.groovy then add these lines of Groovy codes.

package com.djamware.groovyrest.entity

import javax.persistence.CascadeType
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.OneToMany
import javax.persistence.Table

@Entity
@Table(name = 'product')
class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id
    String prodName
    String prodDesc
    Double prodPrice
    String prodImageUrl

    @OneToMany(
            fetch = FetchType.EAGER,
            cascade = CascadeType.ALL,
            orphanRemoval = true,
            mappedBy = 'product'
    )
    List<Variant> variants
}

As you see, that entity class is very simple because all constructors, getters, and setters are handled by Groovy. We still using standard JPA notation for entity class type, ID generation, and one-to-many relationships. Next, open and edit com/djamware/groovyrest
entity/Variant.groovy then add these lines of Groovy codes.

package com.djamware.groovyrest.entity

import com.fasterxml.jackson.annotation.JsonIgnore

import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.ManyToOne
import javax.persistence.Table


@Entity
@Table(name = "variant")
class Variant {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id
    String variantName
    String variantColor
    Double variantPrice

    @ManyToOne(
            fetch = FetchType.LAZY,
            optional = false
    )
    @JsonIgnore
    Product product
}

We are using @JsonIgnore to prevent a whole Product entity persist to the Variant entity. So, it just persists the Product ID only.


Step #3: Create Groovy Repository Interface

We will extend the Product repository with Spring Data PagingAndSortingRepository and the Variant with CrudRepository. First, create a new package com/djamware/groovyrest/repository then add to that package these Groovy files (com/djamware/groovyrest/repository/IProductRepository.groovy and com/djamware/groovyrest/repository/IVariantRepository.groovy). Open and edit com/djamware/groovyrest/repository/IProductRepository.groovy then replace all Groovy codes with these lines of Groovy codes.

package com.djamware.groovyrest.repository

import com.djamware.groovyrest.entity.Product
import org.springframework.data.repository.PagingAndSortingRepository
import org.springframework.stereotype.Repository

@Repository
interface IProductRepository extends PagingAndSortingRepository<Product, Long> {}

Open and edit com/djamware/groovyrest/repository/IVariantRepository.groovy then replace all Groovy codes with these lines of Groovy codes.

package com.djamware.groovyrest.repository

import com.djamware.groovyrest.entity.Variant
import org.springframework.data.repository.CrudRepository

interface IVariantRepository extends CrudRepository<Variant, Long> {}


Step #4: Create Groovy Service and Implementation

Now, we will put all CRUD logic in the Groovy services. We will only operate CRUD in Products that included variants too. For that, create the new packages com/djamware/groovyrest/service, com/djamware/groovyrest/service/impl and a Groovy class and interface files (com/djamware/groovyrest/service/IProductService.groovy and com/djamware/groovyrest/service/impl/ProductServiceImpl.groovy). Open and edit com/djamware/groovyrest/service/IProductService.groovy then replace all Groovy codes with this.

package com.djamware.groovyrest.service

import com.djamware.groovyrest.entity.Product

interface IProductService {
    List<Product> findAll()

    Product findById(Long productId)

    Product saveProduct(Product product)

    Product updateProduct(Long productId, Product product)

    Product deleteProduct(Long productId)
}

Next, open and edit com/djamware/groovyrest/service/impl/ProductServiceImpl.groovy then replace all Groovy codes with this.

package com.djamware.groovyrest.service.impl

import com.djamware.groovyrest.entity.Product
import com.djamware.groovyrest.repository.IProductRepository
import com.djamware.groovyrest.service.IProductService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class ProductServiceImpl implements IProductService {
    @Autowired
    IProductRepository productRepository

    @Override
    List<Product> findAll() {
        productRepository.findAll()
    }

    @Override
    Product findById(Long productId) {
        productRepository.findById productId get()
    }

    @Override
    Product saveProduct(Product product) {
        product.variants?.each { it.product = product }
        productRepository.save product
    }

    @Override
    Product updateProduct(Long productId, Product product){
        def existingProduct = findById(productId)
        existingProduct.with {ep ->
            prodName = product.prodName
            prodDesc = product.prodDesc
            prodPrice = product.prodPrice
            prodImageUrl = product.prodImageUrl
        }
        def variantsToRemove = []
        existingProduct.variants.each {
            def v = product.variants.find { vr -> vr.id == it.id }
            if (v == null) variantsToRemove.add(it)
            else it.variantName = v.variantName
        }
        existingProduct.variants.removeAll(variantsToRemove)
        product.variants.each {
            if (it.id == null) {
                it.product = existingProduct
                existingProduct.variants.add(it)
            }
        }
        productRepository.save existingProduct
    }

    @Override
    Product deleteProduct(Long productId){
        productRepository.deleteById productId
    }
}

All CRUD operation is standard CRUD operation. The save method only uses 2 lines which save Variants and Products all at once. To save all variants from the REST request payload, simply iterate through the product object then set the product for each variant. If you don't add @JsonIgnore annotation in the many-to-one relationship in the Variant entity, the products will loop to save.

Update scenario compares the existing variants in an existing product entity with variants in a product that get from REST request payload. If not exist then the existing variants will be prepared for removal. Then compare product variants from payload with the existing product variants by ID. If found then update the variant, else then add a new variant to the product. So, in the update operation, JSON payload should be included ID, for the new variant ID value may be null.


Step #4: Create Groovy RESTful Controller

The Groovy REST controller will handle all requests and responses in JSON format. Also, specify the endpoint path for all entities and each CRUD operation. Create a new package com/djamware/groovyrest/controller and a Groovy file com/djamware/groovyrest/controller/ProductController.groovy. Open and edit that file then replace all Groovy codes with this.

package com.djamware.groovyrest.controller

import com.djamware.groovyrest.entity.Product
import com.djamware.groovyrest.service.impl.ProductServiceImpl
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("product")
class ProductController {
    @Autowired
    ProductServiceImpl ProductService

    @GetMapping
    List<Product> getAllProductList(){
        ProductService.findAll()
    }

    @PostMapping
    Product saveProduct(@RequestBody Product product){
        ProductService.saveProduct product
    }

    @PutMapping('/{productId}')
    Product updateProduct(@PathVariable Long productId, @RequestBody Product product){
        ProductService.updateProduct productId, product
    }

    @DeleteMapping('/{productId}')
    deleteProduct(@PathVariable Long productId){
        ProductService.deleteProduct productId
    }

    @GetMapping('/{productId}')
    Product getProductById(@PathVariable Long productId){
        ProductService.findById productId
    }
}

There's nothing special here, just the Spring Boot standard REST controller.


Step #5: Run and Test the Spring Boot Groovy PostgreSQL REST API

Before running this Spring Boot Groovy application, make sure the PostgreSQL is running. Next, run this application by type this command in the terminal.

gradle bootRun

Start the Postman, then create a POST request http://localhost:8080/api/v1/product in JSON content-type headers. Use the raw data payload as below then click the Send button.

postman-post

Here is the GET method with the same request URL and headers.

postman-get

Here is the GET by ID method with the same request URL with additional URL parameters and the same headers.

postman-get-by-id

Here is the UPDATE method with the same request URL with additional URL parameters, headers, and body except for the variants array of object should include the ID.

postman-put

Here is the DELETE method with the same request URL with additional URL parameters and the same headers.

postman-delete

That it's, the Spring Boot Groovy PostgreSQL CRUD REST API. You can get the full source codes from our GitHub.

That just the basic. If you need more deep learning about Groovy and Grails you can take the following cheap course:

Thanks!