Spring Boot, a persistent ORM and a consistent Database!

Abirami Sukumaran
Google Cloud - Community
9 min readSep 25, 2023

Spring Boot on Google Cloud: Part 4!

Google Cloud logo and Spring Data logo in union

You would have figured out what I am going to talk about, from the title. Exactly! It is a quick hands-on blog on building a healthy Java Spring Boot web app on Google Cloud with Cloud Spanner as the database and Spring Data Spanner module for the ORM layer.

Ok now, first things first. Let’s get the basics out of the way as usual!

A Quick Recap!

I know it’s been while since part 3. If you are not sure where to start, take a look at the previous blogs of this series: Spring Boot and Google Cloud basics and building your first app, building REST API apps with Spring Boot and deploying Spring Native apps serverlessly on Google Cloud. This is the second last part and brace yourself to become a full stack Java developer very soon!

What is ORM and why is it needed?

Traditionally, developers had to write complex SQL queries and mapping code to transform data between their Java objects and relational database tables. This process was not only time-consuming but also prone to errors and inconsistencies in updates to application code, particularly when there are changes to the database schema.

This is where Object-Relational Mapping (ORM) comes into play. ORM is a programming technique that bridges the gap between the object-oriented programming and relational databases. It enables developers to work with database entities as if they were regular Java objects, abstracting away the intricacies of SQL queries and data mapping.

In essence, ORM simplifies database interactions, making the development process more efficient and less error-prone.

Spring Boot and ORM

Spring Boot provides extensive support for JPA (Java Persistence API), Hibernate and more such widely adopted ORM standards. In this implementation we will use Spring Data Spanner module as the ORM layer that connects the app and the data. It has features similar to that of Spring Data JPA and Hibernate ORM, with annotations designed for Spanner.

By incorporating ORM into Spring Boot apps, developers can improve code readability (as we use high level Java objects), boost productivity (as an application developer, we no longer focus on writing complex database queries), enhance portability (easily switch between different databases with minimal code change) and achieve consistency (as ORM layer enforces consistency in data operations). This is exactly why I said our app is going to be healthy.

Cloud Spanner

Google Cloud Spanner redefines what a database can achieve in terms of scalability, global distribution, and transactional consistency. It is not your typical relational database management system (RDBMS) but rather a globally distributed, horizontally scalable, and strongly consistent database service. You can use it for handling planet scale data or for those use cases that you need granular sizing as well. It scales as you need and is capable of horizontal and vertical scaling. Evidently, it is also my favorite database and we would look at integrating our Spring Boot application with Cloud Spanner, in detail through the rest of this blog.

Spanner Setup, DDL and DML

Before creating the app, let’s complete the database setup by creating a Cloud Spanner instance, database and table. You can refer to this blog to read more in detail about Cloud Spanner features, DDL, DML and more. You can follow the steps below to create the database objects required for this project:

a. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
b. Make sure that billing is enabled for your Cloud project. Learn how to check if billing is enabled on a project.
c. Enable the Cloud Spanner API for the project.
d. Create an instance by clicking CREATE INSTANCE on the instances page.
e. Enter the details as shown in the following image and click CREATE:

Create a Cloud Spanner Instance

f. Once created, from the instance overview page, click CREATE DATABASE
g. Provide the name of the database as “first-spanner-db” and enter the following DDL in the DDL TEMPLATES section and click SUBMIT:

CREATE TABLE Yoga_Poses(
Pose_Id INT64 NOT NULL,
Name STRING(1024),
Breath STRING(1024),
Description STRING(1024)
) PRIMARY KEY(Pose_Id);

The database and table should be created as a result of the last step. Now let’s insert a few rows into the Yoga_Poses table so we can go about building our Spring Boot application.
h. Click Spanner Studio on the Database pane on the left and open a new query editor tab as shown in the image below:

Spanner Studio with Query Editor showing Insert Queries

i. Run the following INSERT queries


INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(1, 'STAND', 'INHALE AND EXHALE',
'STAND WITH YOUR FEET HIP WIDTH APART AND ARMS RESTING BY THE SIDES');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(2, 'PLANK', 'INHALE OR EXHALE',
'PLANT YOUR TOES AND PALMS ON THE MAT WITH BODY PARALLEL TO THE GROUND');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(3, 'SIT', 'INHALE AND EXHALE',
'SIT ON THE FLOOR LEGS CROSSED');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(4, 'BEND', 'EXHALE',
'FOLD FORWARD AS YOU STAND, HANDS REACHING TO THE FLOOR');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(5, 'PUSH UP', 'EXHALE',
'PLANK WITH ELBOWS ON MAT');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(6, 'SEATED FORWARD BEND', 'EXHALE',
'FOLD FORWARD AS YOU SIT, HANDS TRYING TO REACH THE FEET');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(7, 'LUNGE', 'EXHALE',
'ONE LEG TO THE FRONT 90 DEGREES TO THE FLOOR AND THE BACK LEG STRAIGHT');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(8, 'COURTESY LUNGE', 'INHALE',
'ONE LEG TO THE FRONT 90 DEGREES TO THE FLOOR AND THE BACK KNEE TOUCHING THE FLOOR');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(9, 'BANK BEND', 'INHALE',
'STAND WITH ARMS UP AND BODY BENT BACKWARDS, ARCHING YOUR SPINE, LOOKING AT THE SKY');

INSERT INTO Yoga_Poses(Pose_Id, Name, Breath, Description)
VALUES(10, 'BICEP ACTION', 'INHALE AND EXHALE',
'CURL, PRESS AND WORK YOUR BICEPS');

Now we have our Spanner instance, database, table and data created and ready for the application.

Build a Yoga Pose app with Spring Boot and Spanner

I hope you have already familiarized yourself with Spring Boot and Google Cloud console in blog 1 of this series. If not quickly read that short one up, so you are aware of how to launch Google Cloud Shell Terminal and execute the rest of the steps in this blog.

Step 1 — Bootstrap your Spring Boot Application

Run the below command in Cloud Shell Terminal:

curl https://start.spring.io/starter.tgz -d dependencies=cloud-gcp,web,lombok -d baseDir=spanner-springboot -d javaVersion=11 -d bootVersion=2.7.1 -d type=maven-project | tar -xzvf -

You should see the project created and the below structure in the Cloud Shell Editor:

Cloud Shell Editor with spanner-springboot Project

Make sure you see the Spanner dependency in pom.xml. If not, add the below dependency to the <dependencies> section:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gcp-starter-data-spanner</artifactId>
<version>1.2.8.RELEASE</version>
</dependency>

Leave the rest of the file as it is. Save your changes to the pom.xml file.

Step 2 — Create an Entity Class

In Spring Boot, entities are Java classes that represent database tables. Create a simple entity class at “../spanner-springboot/src/main/java/com/example/demo/Yoga.java” to map to the a Yoga_Poses table in our Spanner database and paste the following code in it:

package com.example.demo;

import lombok.Data;
import org.springframework.cloud.gcp.data.spanner.core.mapping.*;

@Table(name="Yoga_Poses")
@Data
class Yoga {
@PrimaryKey(keyOrder = 1)

@Column(name="Pose_Id")
private int poseId;

public int getPoseId() {
return poseId;
}
public void setPoseId(int poseId) {
this.poseId = poseId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBreath() {
return breath;
}
public void setBreath(String breath) {
this.breath = breath;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Column(name="Name")
private String name;

@Column(name="Breath")
private String breath;

@Column(name="Description")
private String desc;
}

In this class Yoga_Poses above, you can see the annotations for table, column, data and primary key. The @Data annotation in Lombok is like a helpful assistant for developers. When you use @Data, it tells your development and build tools to generate boilerplate code for you automatically, behind the scenes.

Step 3 — Create a Repository Interface

Repositories in Spring Data Spanner are interfaces that provide methods for CRUD operations on entities. Create a repository interface for the Yoga entity as YogaRepository.java at the location “../spanner-springboot/src/main/java/com/example/demo/YogaRepository.java” with the following content:

package com.example.demo;

import org.springframework.cloud.gcp.data.spanner.repository.*;
import org.springframework.stereotype.*;

@Repository
public interface YogaRepository extends SpannerRepository<Yoga, String> {
}

The interface extends the SpannerRepository<Yoga, String> where Yoga is the domain class and String is the Primary Key type. Spring Data will automatically provide CRUD access through this interface and you won’t need to create any additional database query code.

Step 4 — Create a REST Controller

Controller classes handle the HTTP requests coming to the application endpoint. Create a REST Controller class to implement basic “getAll” from the database method, named “YogaController.java” in the location: “../spanner-springboot/src/main/java/com/example/demo/YogaController.java” with the following content:

package com.example.demo;

import com.example.demo.Yoga;
import org.springframework.stereotype.Controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
import org.springframework.http.ResponseEntity;
import java.util.List;

@RestController
public class YogaController {
private final YogaRepository yogaRepository;

YogaController(YogaRepository yogaRepository) {
this.yogaRepository = yogaRepository;
}

@GetMapping("/")
public List<Yoga> getAllBooks() {
return (List<Yoga>) yogaRepository.findAll();
}
}

Alternatively, for a more layered approach, you can separate the controller from implementation and have the implementation separately in a Service class (YogaService) to invoke the findAll() method from the YogaRepository interface. Try this out for the rest of the REST API methods (getById, put, post etc.).

Step 5 — Configure Application Properties

In the application.properties file at “../spanner-springboot/src/main/resources/application.properties”, configure Spanner database connection information. Paste the content below and replace the place-holder attributes <<your-instance-name>> and <<your-db-name>> with your values. In this case, it will be my-first-spanner-instance and first-spanner-db respectively.

spring.cloud.gcp.spanner.instance-id=<<your-instance-name>>
spring.cloud.gcp.spanner.database=<<your-db-name>>
spring.main.allow-bean-definition-overriding=true

Save it. You are now ready with your application set up. Let’s test our first Spring Boot + Spanner + ORM Integration.

Step 6 — Build and run your Spring Boot App

Run the command below in Cloud Shell Terminal:

./mvnw package
./mvnw spring-boot:run

You should see that the build is successful with ./mvnw package and the app is now running locally with the ./mvnw soring-boot:run command.

At this point, you should be able to click “Web Preview” button from the top right corner of your Cloud Shell Terminal and view the output in a browser or you can move on to the next step where you deploy the app in Cloud Run and access it from Google Cloud with a REST endpoint.

Step 7 — Deploy your Spring Boot App Serverlessly

Refer to the “Deploy your app on Google Cloud Serverlessly!” section of the part 2 of the blog series for a more detailed explanation for containerizing, registering and deploying your app with Jib, Artifact Registry and Cloud Run. For a quick one-step deploy, run the following command from Cloud Shell Terminal:

gcloud run deploy --source .

Don’t forget the “.” at the end, it is part of the command ;) that specifies that your source is right there in the current working directory.

It should prompt you to enter values for SERVICE NAME, REGION, ALLOW UNAUTHENTICATED INVOCATIONS. Enter the values and you should be good to go, as seen in the image below:

Cloud Shell Terminal withthe deployed Cloud Run Service URL

Voila, you have the deployed HTTP REST Endpoint to your Spring Boot + Spanner app running on Google Cloud Run. Click that and view the data stored in our Yoga_Poses table as a response to the API’s GET method:

Screenshot of Chrome Browser showing URL’s GET Response

Conclusion

Woohoo! That’s the end of your blog 4 of the 5-part Java Spring Boot on Google Cloud series. I challenge you to extend this implementation to try out the other REST API methods in a Service class or in the Controller class to perform INSERT, UPDATE and DELETE operations on the data. Refer the blog for details on implementing these methods.

With Spanner, you can easily monitor the web application we just created to detect query performance issues, identify high latency transactions, get detailed query insights and query plan samples at different components and layers of your application. Learn about it here and here.

--

--

Abirami Sukumaran
Google Cloud - Community

Developer Advocate Google. With 18 years in data and software dev leadership, I’m passionate about addressing real world opportunities with technology.