Enterprise Java

Spring Boot Microservices , Docker and Kubernetes workshop – part1

In this series of workshop we will build few micro services using spring boot, docker and then deploy them into kubernetes. So, Lets get started.

First we need to define a problem statement to begin with. Lets say we want to build a order management system.

Identifying domains

First step would be to understand what domains are required, for simplicity lets assume we need the following domains:

Orders
Products
Customers or Users
Shopping Cart

Now that we know what we are building, let’s start developing.

In this workshop, we would be using the following

Spring boot for micro services
Postgres for Database
Gradle for build
Docker for containers

First Microservice : Products
Lets build our first micro-service for products, we will call it product service, this will contain the details of the products.

Step 1: Set up spring boot application using spring initialiser.
Go to https://start.spring.io/ and genereate a gradle project with Java and SpringBoot 2.1.0 And provide the following values :

group id : com.anirudhbhatnagar
artifact : productService
dependecies : Web, Jpa, postgresSQL

Click generate project and download the zipped project. Create a new directory called “order_management_system”. Unzip the project in a folder and copy its contents into a this new directory.

Import the project into your favourite IDE and we are good to start. Check if the setup is working fine by running the project in a terminal:

./gradlew build

The build would fail with DataSourceBeanCreationException, this happened because we added PostgresSQL dependency in our project but did not configure the data source by giving the DB credentials and its uri. Lets do that in next step.

Step 2: Configure database
We need a database to persist the product details for the product service.
For this we need 2 things :
– A running postgres database
– Configure its details in spring
Lets first create a local postgres database. We can use a docker image to have a local postgres DB running. In order to have a postgres database server running as docker image, we need to have docker in our system. Use this link to install docker in your mac (Similar links can be found for Windows and Linux).
Once docker is installed in your machine. Pull a latest postgres image and run it in your local. We will also initialise a database with username and password to be used. Run the following command in your terminal :

$ docker run --name oms_postgres -p 5432:5432 -e POSTGRES_USER=dbuser -e POSTGRES_DB=products_db -e POSTGRES_PASSWORD=password -d postgres

This will start a postgres server on port 5432 in your local and initialise an empty DB “postgres_db” with username “dbuser” and password “password”. Once the database is up and running, we will now configure the datasource our spring boot application. One the ways and perhaps the easiest one with spring boot is to define data source URI and database credentials in the application.properties file. Spring boot will auto configure the data source using these credentials.

Open the application.properties file in the project and add below :

spring.datasource.url=jdbc:postgresql://localhost:5432/products_db
spring.datasource.username=dbuser
spring.datasource.password=password
spring.jpa.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

Now, that we have configured the database for our application, lets run the gradle build again. Build :

./gradlew build

If everything is fine, then this time the build should pass. Run :

./gradlew bootRun

Now we will have an application running at : http://localhost:8080/ But as we have not implemented any service it will give a 404. In order to get it working, lets add some code.

NOTE : if you are using postgres version you may get this error :

java.sql.SQLFeatureNotSupportedException: Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.

This exception appears because JPA (Hibernate) supported by Atomikos is trying to verify PostgreSQL CLOB feature. This feature is not implemented by JDBC driver, so driver throws an unimportant exception. to fix this, add following to your application.properties file :

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true

This will disable driver’s feature detection (we will not be using this feature anyways)

Step 3: Add the code in product service
Now that our service and database is setup, we can start writing some code for our product service. Make a package with the name : “domain” inside the package “com.anirudhbhatnagar.productService” and create a new Java class “Product” with attributes:

id
name
description
sku

Lombok
We would use Lombok to add constructors, getter, setter, and builder methods for our bean. To use lombok add its dependency to build.gradle file :

compileOnly 'org.projectlombok:lombok:1.18.4'

The add the annotations on the class “Product”

@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
    private String description;
    private String sku;
}

The meaning of annotations:
1. @Entity tell spring boot JPA to treat this class as an entity and persist it the database.
2. @Builder – this lmobok annotation adds builder method to our class for creating objects using builder pattern.
3. @AllArgsConstructor – this lmobok annotation adds an all arguments constructor to the class, Builder method needs this method.
4. @NoArgsConstructor – this lmobok annotation adds a default constructor to the class, JPA needs this constructor to fetch the entities.
5. @Getter – this lombok annotation adds getters to all the fields in the class, this is required to fetch individual attributes of the product, this is also used by Jackson to serialise/deserialise the fields.

And in order to create this table in the database, we need to set jpa hibernate auto-ddl as true. To do that add the following line to application.properties file :

spring.jpa.hibernate.ddl-auto=create

We also added : @GeneratedValue and @Id to the field Id, to tell hibernate to auto generate value for the id when creating a new entry in the table.

Add Controller
Add a controller to implement expose web services and serialise/deserialise the request using Jackson. Make a package with the name : “controller” inside the package “com.anirudhbhatnagar.productService” and create a new Java class “ProductController” inside it.

Annotate the class with “@RestController” to extend this class into a Servlet which exposes the webservices. Create the endpoints with the annotation ” @GetMapping”

@RestController
public class ProductController {

    @GetMapping("/products")
    public List getProducts() {
        return Collections.EMPTY_LIST;
    }

    @PostMapping("/products")
    public Product save(@RequestBody Product product) {
        return null;
    }
}

Add Repository
Add JPA repository to persist products in the database. Make a package with the name : “repository” inside the package “com.anirudhbhatnagar.productService” and create a new interface “ProductRepository” inside it:

public interface ProductRepository extends JpaRepository {
}

Inject productRepository into ProductController so that we can use productRepository in ProductController to pass product request object received in controller to repository to fetch and persist.

@RestController
public class ProductController {

    private ProductRepository productRepository;

    @Autowired
    public ProductController(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @GetMapping("/products")
    public List getProducts() {
        return productRepository.findAll();
    }

    @PostMapping("/products")
    public Product save(@RequestBody Product product) {
        return productRepository.save(product);
    }
}

Now we have product service up and running with following endpoints :

GET /products – gets all the products
POST /products – creates a new product

See the entire code here.

Dockerise the app
Create a file named “dockerFile” in the root folder of the application and add the following contents into it :

FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY build/libs/*.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
EXPOSE 8080

Build :

docker build .

Run :

docker run -p 8080:8080 [image-id]

This should start a service at localhost:8080

Test the application :
Using postman or any other similar tool, submit this request to create a product :

Http POST http://localhost:8080/products. Header : Content-Type application/json

{
"name" : "Nike shoes",
"description" : "mens shoes size 10",
"sku" : "1234asc"
}

And the products can be fetched by : GET http://localhost:8080/products

In the next workshop, we would look into the following :

Spring cloud, Ribbon for Service Discovery and client side load balancing
OpenFeign Client
Kubernetes for container management
API gateways

Published on Java Code Geeks with permission by Anirudh Bhatnagar, partner at our JCG program. See the original article here: Spring Boot Microservices , Docker and Kubernetes workshop – part1

Opinions expressed by Java Code Geeks contributors are their own.

Anirudh Bhatnagar

Anirudh is a Java programmer with extensive experience in building Java/J2EE applications. He has always been fascinated by the new technologies and emerging trends in software development. He has been involved in propagating these changes and new technologies in his projects. He is an avid blogger and agile enthusiast who believes in writing clean and well tested code.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

15 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Mammer
Mammer
5 years ago

Thank you

Kishan
Kishan
5 years ago

Can you please explain relationship between entities?

anirudh Bhatnagar
anirudh Bhatnagar
5 years ago
Reply to  Kishan

Explained in the part 2 at anirudhbhatnagar.com

Amit Shah
Amit Shah
5 years ago

Excellent Workshop! Only question is, why my data is not saved in the database. I created a container for product service and it worked great, but when I destroyed it (not the db one) and restarted, It was expecting data back from the db, but no data was fetched.

Anirudh Bhatnagar
Anirudh Bhatnagar
5 years ago
Reply to  Amit Shah

Change this in application.properties file :
spring.jpa.hibernate.ddl-auto=update
this auto-ddl hibernate property when set to “create” will create the DB every time you run the application, hence you loose the data.

ScottF
ScottF
5 years ago

Great workshop- was able to get it up and running on docker w/ help of the excellent notes. I look forward to part 2!

Ibrahima
Ibrahima
5 years ago

Thanks for this workshop, very interesting.

Do you plan to publish deployment part with kubernetes?

Thanks

marko
marko
5 years ago

After executing last command ‘docker run -p 8080:8080 [image-id]’
I get:
org.postgresql.util.PSQLException: Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.

I’m able to connect to postgre container with DBeaver, connection work, why another container cant access it?

Jorge Aguilar
Jorge Aguilar
5 years ago
Reply to  marko

I had the same problem but I changed property spring.datasource.url to use Postgres container IP address instead of localhost. You could use output from: docker inspect oms_postgres | grep -i ipaddress to identify IP address.

5 years ago
Reply to  Jorge Aguilar

or just change it to host’s ip since host’s 5432 is mapping to container’s 5432, so the same

Tilak
Tilak
5 years ago
Reply to  Jorge Aguilar

I’m also facing the same problem: “org.postgresql.util.PSQLException: Connection to localhost:5432 refused.” when running the ‘docker run -p 8080:8080 [image-id]’

I’m running these containers on Windows. I inspected Postgres container, but I don’t see any IP address: “IPAddress”: “”
I’m able to connect to the Postgre container through SQuirell SQL client.

Pls advise..

Valentino Pezzano
Valentino Pezzano
5 years ago

Hi Anirudh.
I am using Spring Boot 2.1.0 and testing with Spring STS.
When saving a product, I noticed that the controller got a Product object with all null fields. I am using Postman to send the request. I had to add the annotation @JsonProperty on top of the fields name, description and sku in order to get this working.

Jorge Aguilar
Jorge Aguilar
5 years ago

Hi Valentino… I also have the same problem when running project from the IDE, but it works when running from the command line (./gradlew bootRun) and it also is OK when running from the container.

Thanks for the suggestion.

thiago
thiago
5 years ago

Where is the part 2?!!!

Back to top button