How to Build a GraphQL API with Java and Spring Boot

How to Build a GraphQL API with Java and Spring Boot

In this tutorial, I’ll show you how to use Java and Spring Boot to build a GraphQL API. How to GraphQL in Java. Create a GraphQL API with Java and Spring Boot. Run Your Java GraphQL API. Test Your Java GraphQL API with JUnit 5. Secure Your Java GraphQL API. Further Reading on GraphQL and Java.

REST APIs are hard to design so they serve multiple clients well. As each client has their own needs in terms of data searching, filtering and which fields they want, a traditional REST API will provide a single version of an entity and the client has the responsibility of navigating through multiple endpoints and correlate the data on their side to build the data they want.

GraphQL was developed by Facebook to overcome the shortcomings they found with REST APIs. It’s a query language for APIs and a runtime for fulfilling those queries with your existing data. By providing a complete and understandable description of the data in your API, clients can leverage that and have the power to ask for exactly what they need. By exposing the schema, it makes it easier to evolve APIs over time. Clients can still navigate the schema the way they need even if you add more fields and relations to it.

In this tutorial, I’ll show you how to use Java and Spring Boot to build a GraphQL API. I’ll also show how to test your GraphQL API using Java’s most popular testing library: JUnit 5.

Create a GraphQL API with Java and Spring Boot

Let’s start with an initialized app by going to Spring Initializr and defining your app data as follows:

  • Project: Maven Project
  • Language: Java
  • Spring Boot: 2.2.2
  • Project Metadata:
    • Group: com.oktadeveloper
    • Artifact: graphqldemo
  • Dependencies:
    • Add Spring Web
    • Add Spring Data JPA
    • Add H2 Database

You may also follow this link, it will take you to a pre-configured Spring Initializr page.

Expand the downloaded package and add GraphQL SPQR as a dependency to your pom.xml:

<dependency>
    <groupId>io.leangen.graphql</groupId>
    <artifactId>graphql-spqr-spring-boot-starter</artifactId>
    <version>0.0.4</version>
</dependency>

Then create a Food entity class:

package com.oktadeveloper.graphqldemo;

import io.leangen.graphql.annotations.GraphQLQuery;

import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Entity;

@Entity
public class Food {

    @Id @GeneratedValue
    @GraphQLQuery(name = "id", description = "A food's id")
    private Long id;

    @GraphQLQuery(name = "name", description = "A food's name")
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Food{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
    }

}

Notice that you are already using GraphQL SPQR (GraphQL Schema Publisher & Query Resolver, pronounced like speaker) annotations (i.e. @GraphQLQuery) on the entity. This is how it will know to expose those entities in the API.

Create the respective repository:

package com.oktadeveloper.graphqldemo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
interface FoodRepository extends JpaRepository<Food, Long> {
}

In GraphQL you can either define a query which will only load data, or define a mutation which will also change the underlying data that feeds the API. For this sample app, you will define the basic read, save and delete functionality for food entities. For that, create a service class:

package com.oktadeveloper.graphqldemo;

import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLContext;
import io.leangen.graphql.annotations.GraphQLMutation;
import io.leangen.graphql.annotations.GraphQLQuery;
import io.leangen.graphql.spqr.spring.annotations.GraphQLApi;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

@Service
@GraphQLApi
public class FoodService {

    private final FoodRepository foodRepository;

    public FoodService(FoodRepository foodRepository) {
        this.foodRepository = foodRepository;
    }

    @GraphQLQuery(name = "foods") // READ ALL
    public List<Food> getFoods() {
        return foodRepository.findAll();
    }

    @GraphQLQuery(name = "food") // READ BY ID
    public Optional<Food> getFoodById(@GraphQLArgument(name = "id") Long id) {
        return foodRepository.findById(id);
    }

    @GraphQLMutation(name = "saveFood") // CREATE
    public Food saveFood(@GraphQLArgument(name = "food") Food food) {
        return foodRepository.save(food);
    }

    @GraphQLMutation(name = "deleteFood") // DELETE
    public void deleteFood(@GraphQLArgument(name = "id") Long id) {
        foodRepository.deleteById(id);
    }

    @GraphQLQuery(name = "isGood") // Calculated property of Food
    public boolean isGood(@GraphQLContext Food food) {
        return !Arrays.asList("Avocado", "Spam").contains(food.getName());
    }

}

Notice that you are also able to define calculated properties to entities. In the above class, you declared the method isGood() as a property that can be queried for each food. You will see ahead that you can read it just like you read the food’s id and name fields.

To initialize the app with sample data, add an ApplicationRunner bean definition in GraphqldemoApplication:

@Bean
ApplicationRunner init(FoodService foodService) {
    return args -> {
        Stream.of("Pizza", "Spam", "Eggs", "Avocado").forEach(name -> {
            Food food = new Food();
            food.setName(name);
            foodService.saveFood(food);
        });
        foodService.getFoods().forEach(System.out::println);
    };
}

Also, add the following line to application.properties to enable the web UI to test the GraphQL API:

graphql.spqr.gui.enabled=true

Run Your Java GraphQL API

Run the project with ./mvnw spring-boot:run. Head over to http://localhost:8080/gui and you should see a web UI to test your GraphQL API. Run a sample query by typing on the left-side panel:

{
  foods {
    id
    name
    isGood
  }
}

Click the play button and you should see a result similar to this:

{
  "data": {
    "foods": [
      {
        "id": 1,
        "name": "Pizza",
        "isGood": true
      },
      {
        "id": 2,
        "name": "Spam",
        "isGood": false
      },
      {
        "id": 3,
        "name": "Eggs",
        "isGood": true
      },
      {
        "id": 4,
        "name": "Avocado",
        "isGood": false
      }
    ]
  }
}

You can also find a specific food by ID using a query like the following:

{ food(id: 1) { name } }

And seeing that result:

{
  "data": {
    "food": {
      "name": "Pizza"
    }
  }
}

Notice that you are able to manipulate the response. On that last query, you asked only for the name of the food and the API didn’t return the id nor the isGood property.

Create a new food by running the saveFood() mutation:

mutation {
  saveFood(food: { name: "Pasta" }) {
    id
    isGood
  }
}

And you will see a result like:

{
  "data": {
    "saveFood": {
      "id": 5,
      "isGood": true
    }
  }
}

If you query all the foods again you should see the newly added “Pasta” there.

Test Your Java GraphQL API with JUnit 5

You can write tests for your API with JUnit 5 and Spring Mock MVC. To do this, you can call your API via HTTP, wrap the query in a JSON object with a single property called "query", and the response should be similar to what you were seeing in the web UI. For example, the following class tests that you can retrieve all registered foods:

package com.oktadeveloper.graphqldemo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class GraphqldemoApplicationTests {

    @Autowired
    MockMvc mockMvc;

    @Test
    void listFoods() throws Exception {
        String expectedResponse = "{\"data\":{\"foods\":[" +
                "{\"id\":1,\"name\":\"Pizza\",\"isGood\":true}," +
                "{\"id\":2,\"name\":\"Spam\",\"isGood\":false}," +
                "{\"id\":3,\"name\":\"Eggs\",\"isGood\":true}," +
                "{\"id\":4,\"name\":\"Avocado\",\"isGood\":false}" +
                "]}}";

        mockMvc.perform(MockMvcRequestBuilders.post("/graphql")
                .content("{\"query\":\"{ foods { id name isGood } }\"}")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().json(expectedResponse))
                .andReturn();
    }

}

You can replace src/test/java/com/.../GraphqldemoApplicationTests.java with the code above and run ./mvnw test to see it in action.

[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

For a client to use your GraphQL API, it just needs to call it as a standard HTTP API: send a POST request with the query or mutation and parse the result as JSON.

Secure Your Java GraphQL API

So far, your API is open to whoever has its endpoint URI. Let’s change that by adding proper security.

Okta offers a very handy Maven plugin to set up your app’s security quickly and easily. First, add Okta as a dependency in pom.xml. While you’re at it, add Spring Security’s testing library.

<dependency>
    <groupId>com.okta.spring</groupId>
    <artifactId>okta-spring-boot-starter</artifactId>
    <version>1.3.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

Then run the Okta Maven plugin from your app’s folder:

./mvnw com.okta:okta-maven-plugin:setup

Answer a few questions (name, email, and company), and it will generate a new Okta account, register a new OIDC application, and add the correct properties into your application.properties file.

If you start your app again, you’ll notice that you can’t run GraphQL queries anymore. That’s because you’re not authenticated.

To authenticate and see your access token (required to use the API), create a very simple controller that displays the access token. Add the following class to your project:

package com.oktadeveloper.graphqldemo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;

@RestController
class MyAccessTokenController {

    @Autowired
    private OAuth2AuthorizedClientService clientService;

    @RequestMapping("/my-access-token")
    String home(Principal user) {
        OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) user;
        String authorizedClientRegistrationId = token.getAuthorizedClientRegistrationId();
        String name = user.getName();
        OAuth2AuthorizedClient client = clientService.loadAuthorizedClient(authorizedClientRegistrationId, name);
        return "token: " + client.getAccessToken().getTokenValue();
    }

}

Start your app again and go to http://localhost:8080/my-access-token. If you are not authenticated, it will present you with a login form. After authenticating, you will see your token displayed on the web page. Copy the token value as you will use it next.

If you want to use the web UI (http://localhost:8080/gui), click on HTTP HEADERS at the bottom left and add the following, replacing <your_access_token> with the actual value of your access token that you got in the previous step:

{ "Authorization": "Bearer <your_access_token>" }

If you are calling the API directly through HTTP, simply add the Authorization header with value Bearer <your_access_token>. You can click the Copy CURL button in the top right of the web UI to see an example.

curl 'http://localhost:8080/graphql' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'DNT: 1' -H 'Origin: http://localhost:8080' -H 'Authorization: Bearer <your_access_token>' --data-binary '{"query":"{\n  foods {\n    id\n    name\n  }\n}"}' --compressed

Or you can use HTTPie:

http POST http://localhost:8080/graphql query='{foods{id,name}}' 'Authorization: <your_access_token>'

Now you have a fully secured GraphQL API!

If you try running your tests (./mvnw test), you will notice they are failing because the API will now answer with 403 Forbidden instead of 200 OK:

$ ./mvnw test
...
[INFO] Results:
[INFO]
[ERROR] Failures:
[ERROR]   GraphqldemoApplicationTests.listFoods:34 Status expected:<200> but was:<403>
[INFO]
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
...

That happens because your tests are not security-aware. To fix that, you need to add the method call .with(SecurityMockMvcRequestPostProcessors.jwt()) to each of your mockMvc.perform() chains, for example:

mockMvc.perform(MockMvcRequestBuilders.post("/graphql")
        .with(SecurityMockMvcRequestPostProcessors.jwt()) // <- ADD THIS LINE
        .content("{\"query\":\"{ foods { id name isGood } }\"}")
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk())
        .andExpect(content().json(expectedResponse))
        .andReturn();

Both MockMvcRequestBuilders.post() and SecurityMockMvcRequestPostProcessors.jwt() can be static imports, so you can make this code a bit easier to read. Add the imports:

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;

Then remove the class names from the test:

mockMvc.perform(post("/graphql")
        .with(jwt())
        ...

The jwt() method instructs the test to inject a JWT authentication and act accordingly as if a user is authenticated.

Below is a full test class that verifies the GraphQL API you wrote works as expected::

package com.oktadeveloper.graphqldemo;

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class GraphqldemoApplicationTests {

    @Autowired
    MockMvc mockMvc;

    @Test
    @Order(0)
    void listFoods() throws Exception {

        String expectedResponse = "{\"data\":{\"foods\":[" +
                "{\"id\":1,\"name\":\"Pizza\",\"isGood\":true}," +
                "{\"id\":2,\"name\":\"Spam\",\"isGood\":false}," +
                "{\"id\":3,\"name\":\"Eggs\",\"isGood\":true}," +
                "{\"id\":4,\"name\":\"Avocado\",\"isGood\":false}" +
                "]}}";

        mockMvc.perform(post("/graphql")
                .with(jwt())
                .content("{\"query\":\"{ foods { id name isGood } }\"}")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().json(expectedResponse))
                .andReturn();
    }

    @Test
    @Order(1)
    void addAndRemoveFood() throws Exception {
        String expectedResponseBefore = "{\"data\":{\"foods\":[" +
                "{\"id\":1,\"name\":\"Pizza\"}," +
                "{\"id\":2,\"name\":\"Spam\"}," +
                "{\"id\":3,\"name\":\"Eggs\"}," +
                "{\"id\":4,\"name\":\"Avocado\"}" +
                "]}}";
        String expectedResponseAfter = "{\"data\":{\"foods\":[" +
                "{\"id\":1,\"name\":\"Pizza\"}," +
                "{\"id\":2,\"name\":\"Spam\"}," +
                "{\"id\":3,\"name\":\"Eggs\"}," +
                "{\"id\":4,\"name\":\"Avocado\"}," +
                "{\"id\":5,\"name\":\"Pasta\"}" +
                "]}}";

        // List foods, expect 'New Food' to not be there
        mockMvc.perform(post("/graphql")
                .with(jwt())
                .content("{\"query\":\"{ foods { id name } }\"}")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().json(expectedResponseBefore))
                .andReturn();

        // Add 'New Food'
        mockMvc.perform(post("/graphql")
                .with(jwt())
                .content("{\"query\":\"mutation { saveFood(food: { name: \\\"Pasta\\\" }) { id name } }\"}")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().json("{\"data\":{\"saveFood\":{\"id\":5,\"name\":\"Pasta\"}}}"))
                .andReturn();

        // List foods, expect 'New Food' to be there
        mockMvc.perform(post("/graphql")
                .with(jwt())
                .content("{\"query\":\"{ foods { id name } }\"}")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().json(expectedResponseAfter))
                .andReturn();

        // Remove 'New Food'
        mockMvc.perform(post("/graphql")
                .with(jwt())
                .content("{\"query\":\"mutation { deleteFood(id: 5) }\"}")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andReturn();

        // List foods, expect 'New Food' to not be there
        mockMvc.perform(post("/graphql")
                .with(jwt())
                .content("{\"query\":\"{ foods { id name } }\"}")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().json(expectedResponseBefore))
                .andReturn();
    }

}

Spring Boot Tutorials - Spring Boot Full Course

Spring Boot Tutorials - Spring Boot Full Course

Spring Boot Tutorials | Full Course - What is Spring? Spring Boot is an open source Java-based framework used to create a Micro Service. Spring Boot contains a comprehensive infrastructure support for developing a micro service and enables you to develop enterprise-ready applications that you can “just run”.

  1. What is Spring ? – 00:05
  2. Dependency Injection? – 05:34
  3. Spring Tool Suite | Spring Boot IDE – 13:40
  4. Spring, Autowire, Dependency Injection – 22:17
  5. Web App using Spring Boot – 40:39
  6. Application Properties File – 55:33
  7. Accepting Client Data – 01:00:11
  8. ModelAndView – 01:08:51
  9. Model Object – 01:16:12
  10. JPA | MVC | H2 Example – 01:20:29
  11. JPA | MVC | H2 Example Part 2 – 01:35:29
  12. Data JPA | MVC | H2 | Query Methods Example – 01:45:12
  13. Data JPA | MVC | H2 | REST Example – 01:54:43
  14. Data JPA | MVC | H2 | REST Example – 02:02:22
  15. Postman | Data JPA | MVC | H2 | REST Example – 02:06:55
  16. Content Negotiation | Data JPA | MVC | H2 | REST – 02:11:29
  17. Spring Boot | MVC | REST Post Example – 2:19:36
  18. Spring Boot | MVC | REST PUT DELETE Example – 02:27:35
  19. Spring Boot Data REST Example – 02:36:30

Spring Boot Tutorials | Full Course:- https://github.com/navinreddy20/Spring-Boot

Learn Spring Framework and Spring Boot - Build a Spring Web Application

Learn Spring Framework and Spring Boot - Build a Spring Web Application: If you're new to the Spring Framework, this is the course you want to start with. This course covers the core of the Spring Framework, the foundation which all of the other Spring Framework projects are built from.

Build a web application using Spring Framework and *Spring Boot *

If you're new to the Spring Framework, this is the course you want to start with. This course covers the core of the Spring Framework, the foundation which all of the other Spring Framework projects are built from.

In this course, you will learn about important key concepts, such as dependency injection and inversion of control, which are used throughout the Spring Framework. Within the Spring Framework, you have the option of using the traditional XML configuration, or the new Java based configuration. I'll show you step by step how to configure Spring Beans using best practices in XML and Java. I'll also show you how to use Spring to persist data into a database, and Spring MVC to show content from the database on a webpage.

By the time we reach the end of this course, you will be able to build a functioning Spring Web Application.

In this course, you will learn about:

  • Dependency Injection and Inversion of Control (IoC) in the Spring Framework.
  • Spring Boot
  • Using Spring Initializr
  • Using Maven to build Spring Projects
  • How to use JUnit and Mockito to test Spring
  • Java and XML Spring Configuration
  • Spring MVC and Thymeleaf with Bootstrap CSS
  • Spring MVC Test
  • JPA / Hibernate
  • Spring JPA and using DAOs
  • Spring Profiles

How to build the MVC Java Web App using Spring Boot and Netbean

How to build the MVC Java Web App using Spring Boot and Netbean

In this tutorial, you'll learn how to build the MVC (Model, View, Controller) Java Web App using Spring Boot and Netbeans 11.1

In this Spring Boot tutorial, we will guide you on how to build the MVC Java web app using Netbeans (we are using Apache Netbeans 11.1). Netbeans IDE development continues by Apache since version 9. You can use Eclipse, Intellij, or JDeveloper as well. By using Spring Boot, building Java web app actually a little bit easier than creating Java projects manually using Maven or Gradle. Especially when we are using Spring Initialzr that will help you to create a set of application bundles with the supported dependencies.

Table of Contents:

  • Step #1: Generate Spring Boot Java Web App
  • Step #2: Create Java Model or Entity Class
  • Step #3: Create Java Repository Interface
  • Step #4: Create Spring MVC Controller
  • Step #5: Create Spring MVC Views
  • Step #6: Run The Spring Boot Java Web App

As you can see in the above table of contents that build MVC Java web app required a few steps. Starting from creating Spring Boot project, Java model, Java repository, Java controller, and Java view. This MVC Java web app also uses a database and we will use an H2 In-Memory database or HSQLDB. For the view, we will use Thymeleaf and Bootstrap. So the following tools, frameworks, and libraries are required for this tutorial:

  1. Java Development Kit (JDK) 8
  2. Gradle
  3. Spring Boot
  4. Spring MVC
  5. Spring Data JPA
  6. H2 Database
  7. Thymeleaf
  8. Webjars Bootstrap
  9. Spring Initializer
  10. Netbeans
  11. Terminal or cmd

We assume that you have to install Netbeans, JDK 8 and Gradle in your machine. So, we need just generate a new Spring Boot Gradle Java Web App.

Step #1: Generate Spring Boot Java Web App

We will create a new Spring Boot Gradle project 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 Initializer web-based Spring project generator then fill the required frameworks and libraries (Spring Web, H2 Database, Thymeleaf, Spring Data JPA, Spring Data JDBC).

After filling all fields, click Generate Project. It will automatically download the zipped project. Next, extract the zipped project to your java projects folder then open the folder from Netbeans IDE a project (use open project menu). Next, from the Projects panel expand the project name (ours: springmvc) then expand Build Scripts and you will see the build.gradle file. Open that file and you will see this project information, plugin, repositories, and dependencies.

plugins {
    id 'org.springframework.boot' version '2.1.9.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'
}

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

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    runtimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Add this dependency for Bootstrap and Thymeleaf Layout Dialect inside dependencies body.

dependencies {
  ...
    compile 'org.webjars:bootstrap:3.3.7'
  compile 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.4.1'
    ...
}

In Netbeans, right-click the project name then click build to build the project for the first time.

Step #2: Create Java Model or Entity Class

We will create a single table of product data for this Spring MVC Java web app. Now, we will implement the term "M" of the Spring MVC by creating a new Java model or entity. Right-click the project name -> New -> Java Class. Fill the class name as Product, the package name as com.djamware.springmvc.models, and leave other fields as default then click the Finish button. The new Java class automatically opened then replace all Java codes with these codes of Java entity, auto-generation ID, required fields, the constructors, and getter and setter.

package com.djamware.springmvc.models;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String prodName;

    private String prodDesc;

    private String prodImage;

    private Double prodPrice;

    public Product() {
    }

    public Product(String prodName, String prodDesc, String prodImage, Double prodPrice) {
        this.prodName = prodName;
        this.prodDesc = prodDesc;
        this.prodImage = prodImage;
        this.prodPrice = prodPrice;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getProdName() {
        return prodName;
    }

    public void setProdName(String prodName) {
        this.prodName = prodName;
    }

    public String getProdDesc() {
        return prodDesc;
    }

    public void setProdDesc(String prodDesc) {
        this.prodDesc = prodDesc;
    }

    public String getProdImage() {
        return prodImage;
    }

    public void setProdImage(String prodImage) {
        this.prodImage = prodImage;
    }

    public Double getProdPrice() {
        return prodPrice;
    }

    public void setProdPrice(Double prodPrice) {
        this.prodPrice = prodPrice;
    }

}
Step #3: Create Java Repository Interface

Next, we have to create a User Repository that extends the JPA Repository for the Product model. Right-click the project name -> New -> Java Interface. Fill the class name as ProductRepository, the package name com.djamware.springmvc.repositories, and leave other fields as default then click the Finish button. That newly created file will open then replace all Java codes with these codes.

package com.djamware.springmvc.repositories;

import com.djamware.springmvc.models.Product;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, Long> {

    Product findByProdName(final String prodName);

}

As you see, the ProductRepository interface now extends the JpaRepository of Product type and it only has an additional query interface of find product by product name. Since Spring Boot 2.1, overriding is disable by default. So, overriding this ProductRepository will result in this error in runtime.

The bean 'productRepository', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.

To fix that, just open and edit Resources/application.properties then add this line to enable overriding.

spring.main.allow-bean-definition-overriding=true
Step #4: Create Spring MVC Controller

Now, it's time for C terms of MVC that means Controller. To create it on Netbeans just right click on project name -> New -> Java Class. Fill the class name with ProductController and the package with com.djamware.springmvc.controllers. Leave other fields as default then click the Finish button. That newly created Java class file will be opened automatically. Replace all Java codes with these codes of @Controller, @RequestMapping, @RequestParam, ProductRepository, and all required CRUD methods that return the page for each CRUD operation.

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.djamware.springmvc.controllers;

import com.djamware.springmvc.models.Product;
import com.djamware.springmvc.repositories.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class ProductController {

    @Autowired
    ProductRepository productRepository;

    @RequestMapping("/product")
    public String product(Model model) {
        model.addAttribute("products", productRepository.findAll());
        return "product";
    }

    @RequestMapping("/create")
    public String create(Model model) {
        return "create";
    }

    @RequestMapping("/save")
    public String save(@RequestParam String prodName, @RequestParam String prodDesc, @RequestParam Double prodPrice, @RequestParam String prodImage) {
        Product product = new Product();
        product.setProdName(prodName);
        product.setProdDesc(prodDesc);
        product.setProdImage(prodImage);
        product.setProdPrice(prodPrice);
        productRepository.save(product);

        return "redirect:/show/" + product.getId();
    }

    @RequestMapping("/show/{id}")
    public String show(@PathVariable Long id, Model model) {
        model.addAttribute("product", productRepository.findById(id).orElse(null));
        return "show";
    }

    @RequestMapping("/delete")
    public String delete(@RequestParam Long id) {
        Product product = productRepository.findById(id).orElse(null);
        productRepository.delete(product);

        return "redirect:/product";
    }

    @RequestMapping("/edit/{id}")
    public String edit(@PathVariable Long id, Model model) {
        model.addAttribute("product", productRepository.findById(id).orElse(null));
        return "edit";
    }

    @RequestMapping("/update")
    public String update(@RequestParam Long id, @RequestParam String prodName, @RequestParam String prodDesc, @RequestParam Double prodPrice, @RequestParam String prodImage) {
        Product product = productRepository.findById(id).orElse(null);
        product.setProdName(prodName);
        product.setProdDesc(prodDesc);
        product.setProdImage(prodImage);
        product.setProdPrice(prodPrice);
        productRepository.save(product);

        return "redirect:/show/" + product.getId();
    }

}
Step #5: Create Spring MVC Views

Now, it's the time of V for the View of MVC pattern. Since we are using the Thymeleaf library for view template, so we can create the same layout for all HTML pages. Thymeleaf is a Java XML/XHTML/HTML5 template engine that can work both in web (servlet-based) and non-web environments. It is better suited for serving XHTML/HTML5 at the view layer of MVC-based web applications, but it can process any XML file even in offline environments. To create the layout, first, create a default.html file on the Resources/templates folder then add or replace it with these lines of codes.

<!DOCTYPE html>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
    <head>
        <meta charset="UTF-8"/>
        <title>Default title for my pages</title>
        <link rel="stylesheet" href="/webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
        <link rel="stylesheet" href="/webjars/bootstrap/3.3.7/css/bootstrap-theme.min.css"/>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>
        <nav class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="#">Spring MVC</a>
                </div>
                <div id="navbar" class="collapse navbar-collapse">
                    <ul class="nav navbar-nav">
                        <li class="active"><a href="/product">Home</a></li>
                    </ul>
                </div><!--/.nav-collapse -->
            </div>
        </nav>

        <div class="container">
            <div class="starter-template" layout:fragment="content"></div>
        </div><!-- /.container -->

        <script src="/webjars/jquery/1.11.1/jquery.min.js"></script>
        <script src="/webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    </body>
</html>

That HTML shows a layout template using Thymeleaf Layout. To making this layout working, make sure you have additional dependencies of Thymeleaf Layout Dialect that previously added in the first steps. All CSS and Javascript files put on that file and called once for all pages that use default.html as layout holder. Also, we call Bootstrap and JQuery on that file. Next, we create a view for the Product list with the name product.html then replace all codes to that file with this.

<!DOCTYPE HTML>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="default">
    <head>
        <title>Product List</title>
    </head>
    <body>
        <div layout:fragment="content" class="row">
            <div class="col-xs-8 col-md-8">
                <h3>
                    <a href="/create" class="btn btn-primary"><span class="glyphicon glyphicon-plus-sign"></span> Product</a>
                </h3>
                <h2>Product List</h2>
                <div class="table-responsive">
                    <table class="table" id="product-table">
                        <thead>
                            <tr>
                                <th>Product Name</th>
                                <th>Product Desc</th>
                                <th>Product Price</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr th:each="product : ${products}">
                                <td><a th:text="${product.prodName}" th:href="@{'/show/' + ${product.id}}"></a></td>
                                <td th:text="${product.prodDesc}"></td>
                                <td th:text="${product.prodPrice}"></td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </body>
</html>

Next, create a view for creating a product form with the name create.html on Resources/templates folder then replace all codes with this.

<!DOCTYPE HTML>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="default">
    <head>
        <title>Create Product</title>
    </head>
    <body>
        <div layout:fragment="content" class="row">
            <div class="col-xs-8 col-md-8">
                <h3>
                    <a href="/product" class="btn btn-lg btn-primary"><span class="glyphicon glyphicon-list"></span> Product</a>
                </h3>
                <h2>Create Product</h2>
                <form action="/save">
                    <div class="form-group">
                        <label for="email">Product Name:</label>
                        <input type="text" class="form-control" name="prodName" />
                    </div>
                    <div class="form-group">
                        <label for="email">Product Description</label>
                        <textarea class="form-control" name="prodDesc" cols="60" rows="3"></textarea>
                    </div>
                    <div class="form-group">
                        <label for="email">Product Price</label>
                        <input type="number" class="form-control" name="prodPrice" />
                    </div>
                    <div class="form-group">
                        <label for="email">Product Image URL:</label>
                        <input type="url" class="form-control" name="prodImage" />
                    </div>
                    <button type="submit" class="btn btn-success">Save</button>
                </form>
            </div>
        </div>
    </body>
</html>

Next, create a view for show product detail with the name show.html on Resources/templates folder then replace all codes with this.

<!DOCTYPE HTML>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="default">
    <head>
        <title>Show Product</title>
    </head>
    <body>
        <div layout:fragment="content" class="row">
            <div class="col-xs-8 col-md-8">
                <h3>
                    <a href="/product" class="btn btn-primary"><span class="glyphicon glyphicon-list"></span> Product</a>
                </h3>
                <h2 th:text="${product.prodName}"></h2>
                <h2><img th:src="${product.prodImage}" width="200" /></h2>
                <dl class="list">
                    <dt>Product Description</dt>
                    <dd th:text="${product.prodDesc}"></dd>
                    <dt>Product Description</dt>
                    <dd th:text="${product.prodPrice}"></dd>
                </dl>
                <form action="/delete">
                    <input type="hidden" name="id" th:value="${product.id}" />
                    <h2><input type="submit" class="btn btn-danger" value="Delete" onclick="return confirm('Are you sure?');" />
                        <a th:href="@{'/edit/' + ${product.id}}" class="btn btn-warning">Edit</a></h2>
                </form>
            </div>
        </div>
    </body>
</html>

Next, create a view for edit product with the name edit.html on Resources/templates folder then replace all codes with this.

<!DOCTYPE HTML>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="default">
    <head>
        <title>Edit Product</title>
    </head>
    <body>
        <div layout:fragment="content" class="row">
            <div class="col-xs-8 col-md-8">
                <h3>
                    <a href="/product" class="btn btn-lg btn-primary"><span class="glyphicon glyphicon-list"></span> Product</a>
                </h3>
                <h2>Edit Product</h2>
                <form action="/update">
                    <div class="form-group">
                        <label for="email">Product Name:</label>
                        <input type="text" class="form-control" name="prodName" th:value="${product.prodName}" />
                    </div>
                    <div class="form-group">
                        <label for="email">Product Description</label>
                        <textarea class="form-control" name="prodDesc" cols="60" rows="3" th:text="${product.prodDesc}"></textarea>
                    </div>
                    <div class="form-group">
                        <label for="email">Product Price</label>
                        <input type="number" class="form-control" name="prodPrice" th:value="${product.prodPrice}" />
                    </div>
                    <div class="form-group">
                        <label for="email">Product Image URL:</label>
                        <input type="url" class="form-control" name="prodImage" th:value="${product.prodImage}" />
                    </div>
                    <input type="hidden" name="id" th:value="${product.id}" />
                    <button type="submit" class="btn btn-success">Save</button>
                </form>
            </div>
        </div>
    </body>
</html>

Next, open and edit static HTML file Resources/static/index.html then replace all codes with this.

<!DOCTYPE HTML>
<html>
    <head>
        <title>Spring MVC Java Web App</title>
    </head>
    <body>
        <h2>Spring MVC Java Web App</h2>
        <p><a href="/product">Product List</a></p>
    </body>
</html>

This index.html is the first page that shows up on the browser after Web Application run. Next, add a little style by creating a new CSS folder inside the static folder then add a file style.css. Add these lines of CSS codes to that CSS file.

body {
  padding-top: 50px;
}
.starter-template {
  padding: 40px 15px;
  text-align: center;
}
Step #6: Run The Spring Boot Java Web App

To run the Spring Boot app inside Netbeans IDE, just click on the play button in the toolbar or right-click the project name in the Project panel then click run. It's the same as typing this command from the terminal.

./gradlew --configure-on-demand -x check bootRun

Here the full Spring Boot MVC Java web app looks like.





That it's, a quick way to create a Java web app using Spring Boot, Data, and MVC in just 5 steps. You can get the full working source code from our GitHub.

That just the basic. If you need more deep learning about Java and Spring Framework you can take the following cheap course:

Thanks!