CRUD in Spring Boot with Spring Data JPA

Tutorial about CRUD operations in Spring Boot with Spring Data JPA.

April 11, 2016 | 4

Tutorial about CRUD operations in Spring Boot with Spring Data JPA. Application developed in this tutorial performs adding, editing, updating and deleting contacts.

We will be using the following to successfully create our application in this tutorial:

  • Spring Web (Spring MVC, Spring Web MVC, a lot)
  • MySQL
  • Spring Data JPA (JPA, Hibernate)
  • Thymeleaf - template engine; bye JSP!
  • Many more, more than 50. With just a few lines in our pom.xml, let Spring Boot provide everything.

Create A Project

Go to Spring Intializr and create a Maven project.

Create Project in Spring Initializr

Once done, hit Generate Project button to save the project.

Unzip the project and start coding. You can use any editor or IDE you prefer. For Eclipse users, you will have to issue the following command via Terminal before importing to Eclipse:

$ cd springbootcrud
$ mvn eclipse:eclipse

For NetBeans users, just click File > Open Project... or click Open Project toolbar, and locate where you unzipped the project. NetBeans natively suports Maven project, no conversion needed.

Dependencies

Updadte your pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>io.github.julianjupiter</groupId>
	<artifactId>springbootcrud</artifactId>
	<version>0.0.1</version>
	<packaging>jar</packaging>

	<name>CRUD in Spring Boot with Spring Data JPA</name>
	<description>CRUD in Spring Boot with Spring Data JPA</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Database Table

Create database in MySQL. Mine is springbootcrud. Add the following table:

CREATE TABLE `contact` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`last_name` varchar(100) NOT NULL,
`first_name` varchar(100) NOT NULL,
`mobile_no` varchar(11) NOT NULL,
`address` varchar(255) NOT NULL,
`date_created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Codes

Property

Insert the following to application.properties

spring.datasource.url = jdbc:mysql://localhost:3306/springbootcrud
spring.datasource.username = root
spring.datasource.password = admin123

Configuration

RepositoryConfiguration.java

package io.github.julianjupiter.springbootcrud.configuration;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableAutoConfiguration
@EntityScan(basePackages = {"io.github.julianjupiter.springbootcrud.domain"})
@EnableJpaRepositories(basePackages = {"io.github.julianjupiter.springbootcrud.repository"})
@EnableTransactionManagement
public class RepositoryConfiguration {
}

Main Class

Application.java

package io.github.julianjupiter;

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

@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

Domain

Contact.java

package io.github.julianjupiter.springbootcrud.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
public class Contact {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    @NotEmpty(message = "Last Name is required.")
    @Size(min = 2, message = "Last Name must be at least 2 characters.")
    private String lastName;
    @NotEmpty(message = "First Name is required.")
    @Size(min = 2, message = "First Name must be at least 2 characters.")
    private String firstName;
    @Column(name = "mobile_no")
    @Size(min = 11, max = 11, message = "Mobile no. must be 11 digits.")
    private String mobileNumber;
    @NotEmpty(message = "Address is required.")
    private String address;

    public Integer getId() {
        return id;
    }

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

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getMobileNumber() {
        return mobileNumber;
    }

    public void setMobileNumber(String mobileNumber) {
        this.mobileNumber = mobileNumber;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

Repository

ContactRepository.java

package io.github.julianjupiter.springbootcrud.repository;

import io.github.julianjupiter.springbootcrud.domain.Contact;
import org.springframework.data.repository.CrudRepository;

public interface ContactRepository extends CrudRepository<Contact, Integer>{

}

Service

ContactService.java

package io.github.julianjupiter.springbootcrud.service;

import io.github.julianjupiter.springbootcrud.domain.Contact;

public interface ContactService {
    Iterable<Contact> getAllContacts();
    Contact getContactById(Integer id);
    Contact saveContact(Contact contact);
    void deleteContact(Integer id);
}

ContactServiceImpl.java

package io.github.julianjupiter.springbootcrud.service;

import io.github.julianjupiter.springbootcrud.domain.Contact;
import io.github.julianjupiter.springbootcrud.repository.ContactRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ContactServiceImpl implements ContactService {

    private ContactRepository contactRepository;

    @Autowired
    public void setContactRepository(ContactRepository contactRepository) {
        this.contactRepository = contactRepository;
    }

    @Override
    public Iterable<Contact> getAllContacts() {
        return this.contactRepository.findAll();
    }

    @Override
    public Contact getContactById(Integer id) {
        return this.contactRepository.findOne(id);
    }

    @Override
    public Contact saveContact(Contact contact) {
        return this.contactRepository.save(contact);
    }

    @Override
    public void deleteContact(Integer id) {
        this.contactRepository.delete(id);
    }

}

Controllers

HomeController.java

package io.github.julianjupiter.springbootcrud.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HomeController {
    @RequestMapping(value = { "", "/", "/home" })
    public String index(Model model) {
        model.addAttribute("activePage", "home");
        return "index";
    }
}

ContactController.java

package io.github.julianjupiter.springbootcrud.controller;

import io.github.julianjupiter.springbootcrud.domain.Contact;
import io.github.julianjupiter.springbootcrud.service.ContactService;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/contacts")
public class ContactController {
    private ContactService contactService;

    @Autowired
    public void setContactService(ContactService contactService) {
        this.contactService = contactService;
    }

    @RequestMapping(value = { "", "/" })
    public String index(Model model) {
        model.addAttribute("activePage", "contacts");
        model.addAttribute("contacts", this.contactService.getAllContacts());
        return "contacts/index";
    }

    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String addContactForm(Contact contact, Model model) {
        model.addAttribute("activePage", "contacts");
        return "contacts/add";
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String addContact(@Valid Contact contact, BindingResult bindingResult, Model model) {
        if (bindingResult.hasErrors()) {
            model.addAttribute("activePage", "contacts");
            return "contacts/add";
        }

        this.contactService.saveContact(contact);
        return "redirect:/contacts";
    }

    @RequestMapping(value = "/view/{id}")
    public String viewContact(@PathVariable Integer id, Model model) {
        model.addAttribute("contact", this.contactService.getContactById(id));
        model.addAttribute("activePage", "contacts");
        return "contacts/view";
    }

    @RequestMapping(value = "/edit/{id}")
    public String editContact(@PathVariable Integer id, Model model) {
        model.addAttribute("contact", this.contactService.getContactById(id));
        model.addAttribute("activePage", "contacts");
        return "contacts/edit";
    }

    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public String updateContact(Contact contact) {
        System.out.println("Contact ID: " + contact.getId());
        this.contactService.saveContact(contact);
        return "redirect:/contacts/view/" + contact.getId();
    }

    @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
    public String deleteContact(@PathVariable Integer id) {
        this.contactService.deleteContact(id);
        return "redirect:/contacts";
    }

}

Views

Fragments

templates/fragments/head.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <link href="../static/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet" media="screen" />
        <link href="../static/css/app.css" th:href="@{/css/app.css}" rel="stylesheet" media="screen" />
    </head>
    <body>
    </body>
</html>

templates/fragments/header.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head></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="#" th:href="@{/}">Spring Boot</a>
                </div>
                <div id="navbar" class="collapse navbar-collapse">
                    <ul class="nav navbar-nav">
                        <li th:class="${activePage == 'home'}? 'active' : ''"><a href="#" th:href="@{/}">Home</a></li>
                        <li th:class="${activePage} == 'contacts'? 'active' : ''"><a href="#" th:href="@{/contacts}">Contacts</a></li>
                    </ul>
                </div>
                <!--/.nav-collapse -->
            </div>
        </nav>
    </body>
</html>

templates/fragments/js.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head></head>
    <body>
        <script src="../static/js/jquery.min.js" th:src="@{/js/jquery.min.js}"></script>
        <script src="../static/js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script>
    </body>
</html>

Home

templates/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Home - CRUD in Spring Boot with Spring Data JPA</title>
        <!--/*/ <th:block th:include="fragments/head :: head"></th:block> /*/-->
    </head>
    <body>
        <!--/*/ <th:block th:include="fragments/header :: body"></th:block> /*/-->
        <div class="container">
            <div class="starter-template">
                <h1>CRUD in Spring Boot with Spring Data JPA</h1>
                <p class="lead">This application demonstrates CRUD application developed with Spring Boot, Spring Data JPA, Thymeleaf and MySQL.<br />
                    <a href="/contacts" class="btn btn-primary btn-lg">Contacts</a>
                </p>
            </div>
        </div>
        <!--/*/ <th:block th:include="fragments/js :: body"></th:block> /*/-->
    </body>
</html>

Contacts

templates/contact/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Contacts - CRUD in Spring Boot with Spring Data JPA</title>
        <!--/*/ <th:block th:include="fragments/head :: head"></th:block> /*/-->
    </head>
    <body>
        <!--/*/ <th:block th:include="fragments/header :: body"></th:block> /*/-->
        <div class="container">
            <div class="row">
                <div class="col-sm-10 col-sm-offset-1">
                    <div class="panel panel-default">
                        <!-- Default panel contents -->
                        <div class="panel-heading">Contacts <a th:href="@{/contacts/add}" class="btn btn-primary"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></a></div>
                        <table class="table">
                            <thead>
                                <tr>
                                    <th>ID</th>
                                    <th>Last Name</th>
                                    <th>First Name</th>
                                    <th>Mobile No.</th>
                                    <th>Address</th>
                                    <th>Action</th>
                                </tr>
                            </thead>
                            <tbody>
                            <div th:if="!${#lists.isEmpty(contacts)}">
                                <tr th:each = "contact : ${contacts}">
                                    <td th:text = "${contact.id}"></td>
                                    <td th:text = "${contact.lastName}"></td>
                                    <td th:text = "${contact.firstName}"></td>
                                    <td th:text = "${contact.mobileNumber}"></td>
                                    <td th:text = "${contact.address}"></td>
                                    <td>
                                        <div class="btn-group" role="group" aria-label="...">
                                            <a th:href="${'/contacts/view/' + contact.id}" class="btn btn-info"><span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span></a>
                                            <a th:href="${'/contacts/edit/' + contact.id}" class="btn btn-success"><span class="glyphicon glyphicon-edit" aria-hidden="true"></span></a>
                                            <a th:href="${'/contacts/delete/' + contact.id}" class="btn btn-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></a>
                                        </div>
                                    </td>
                                </tr>
                            </div>
                            <div th:if="${#lists.isEmpty(contacts)}">
                                <tr>
                                    <td>No contact exists!</td>
                                </tr>
                            </div>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
        <!--/*/ <th:block th:include="fragments/js :: body"></th:block> /*/-->
        <script src="../static/js/app.js" th:src="@{/js/app.js}"></script>
    </body>
</html>

templates/contact/add.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Contacts - CRUD in Spring Boot with Spring Data JPA</title>
        <!--/*/ <th:block th:include="fragments/head :: head"></th:block> /*/-->
    </head>
    <body>
        <!--/*/ <th:block th:include="fragments/header :: body"></th:block> /*/-->
        <div class="container">
            <div class="row">
                <div class="col-sm-8 col-sm-offset-2">
                    <!--<div class="container">-->
                    <div class="panel panel-default">
                        <!-- Default panel contents -->
                        <div class="panel-heading">Add Contact</div>
                        <div class="panel-body">
                            <form action="/contacts/add" th:action="@{/contacts/add}" th:object="${contact}" method="post">
                                <div class="form-group">
                                    <label for="inputLastName">Last Name</label>
                                    <input type="text" class="form-control" id="inputLastName" placeholder="Last Name" th:field="*{lastName}" name="lastName" />
                                    <span  class="help-block" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}">Last Name Error</span>
                                </div>
                                <div class="form-group">
                                    <label for="inputFirstName">First Name</label>
                                    <input type="text" class="form-control" id="inputFirstName" placeholder="First Name" th:field="*{firstName}" name="firstName" />
                                    <span  class="help-block" th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}">First Name Error</span>
                                </div>
                                <div class="form-group">
                                    <label for="inputMobileNumber">Mobile Number</label>
                                    <input type="text" class="form-control" id="inputMobileNumber" placeholder="Mobile No." th:field="*{mobileNumber}" name="mobileNumber" />
                                    <span  class="help-block" th:if="${#fields.hasErrors('mobileNumber')}" th:errors="*{mobileNumber}">Mobile Number Error</span>
                                </div>
                                <div class="form-group">
                                    <label for="inputAddress">Address</label>
                                    <input type="text" class="form-control" id="inputAddress" placeholder="Address" th:field="*{address}" name="address" />
                                    <span  class="help-block" th:if="${#fields.hasErrors('address')}" th:errors="*{address}">Address Error</span>
                                </div>
                                <div class="text-right">
                                    <a href="/contacts" th:href="@{/contacts}" class="btn btn-default">Cancel</a>
                                    <button type="submit" class="btn btn-primary">Save</button>
                                </div>
                            </form>
                        </div>
                        <div class="panel-footer">
                        </div>
                    </div>
                    <!--</div>-->
                </div>
            </div>
        </div>
        <!--/*/ <th:block th:include="fragments/js :: body"></th:block> /*/-->
        <script src="../static/js/app.js" th:src="@{/js/app.js}"></script>
    </body>
</html>

templates/contact/view.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Contacts - CRUD in Spring Boot with Spring Data JPA</title>
        <!--/*/ <th:block th:include="fragments/head :: head"></th:block> /*/-->
    </head>
    <body>
        <!--/*/ <th:block th:include="fragments/header :: body"></th:block> /*/-->
        <div class="container">
            <div class="row">
                <div class="col-sm-8 col-sm-offset-2">
                    <!--<div class="container">-->
                    <div class="panel panel-default">
                        <!-- Default panel contents -->
                        <div class="panel-heading">View Contact</div>
                        <div class="panel-body">
                            <div class="form-group">
                                <label for="inputId">ID</label>
                                <input type="text" class="form-control" id="inputId" placeholder="ID" th:field="*{contact.id}" name="id" disabled="disabled" />
                            </div>
                            <div class="form-group">
                                <label for="inputLastName">Last Name</label>
                                <input type="text" class="form-control" id="inputLastName" placeholder="Last Name" th:field="*{contact.lastName}" name="lastName" disabled="disabled" />
                            </div>
                            <div class="form-group">
                                <label for="inputFirstName">First Name</label>
                                <input type="text" class="form-control" id="inputFirstName" placeholder="First Name" th:field="*{contact.firstName}" name="firstName" disabled="disabled" />
                            </div>
                            <div class="form-group">
                                <label for="inputMobileNumber">Mobile Number</label>
                                <input type="text" class="form-control" id="inputMobileNumber" placeholder="Mobile No." th:field="*{contact.mobileNumber}" name="mobileNumber" disabled="disabled" />
                            </div>
                            <div class="form-group">
                                <label for="inputAddress">Address</label>
                                <input type="text" class="form-control" id="inputAddress" placeholder="Address" th:field="*{contact.address}" name="address" disabled="disabled" />
                            </div>
                            <div class="text-right">
                                <a href="/contacts" th:href="@{/contacts}" class="btn btn-default">Back</a>
                                <a th:href="@{'/contacts/edit/' + ${contact.id}}" class="btn btn-primary">Edit</a>
                            </div>
                        </div>
                        <div class="panel-footer">
                        </div>
                    </div>
                    <!--</div>-->
                </div>
            </div>
        </div>
        <!--/*/ <th:block th:include="fragments/js :: body"></th:block> /*/-->
        <script src="../static/js/app.js" th:src="@{/js/app.js}"></script>
    </body>
</html>

templates/contact/edit.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Contacts - CRUD in Spring Boot with Spring Data JPA</title>
        <!--/*/ <th:block th:include="fragments/head :: head"></th:block> /*/-->
    </head>
    <body>
        <!--/*/ <th:block th:include="fragments/header :: body"></th:block> /*/-->
        <div class="container">
            <div class="row">
                <div class="col-sm-8 col-sm-offset-2">
                    <!--<div class="container">-->
                    <div class="panel panel-default">
                        <!-- Default panel contents -->
                        <div class="panel-heading">Edit Contact</div>
                        <div class="panel-body">
                            <form th:action="@{/contacts/update}" th:object="${contact}" method="post">
                                <input type="hidden" th:field="*{id}" />
                                <div class="form-group">
                                    <label for="inputId">ID</label>
                                    <input type="text" class="form-control" id="inputId" placeholder="ID" th:field="*{id}" disabled="disabled" />
                                </div>
                                <div class="form-group">
                                    <label for="inputLastName">Last Name</label>
                                    <input type="text" class="form-control" id="inputLastName" placeholder="Last Name" th:field="*{lastName}" />
                                </div>
                                <div class="form-group">
                                    <label for="inputFirstName">First Name</label>
                                    <input type="text" class="form-control" id="inputFirstName" placeholder="First Name" th:field="*{firstName}" />
                                </div>
                                <div class="form-group">
                                    <label for="inputMobileNumber">Mobile Number</label>
                                    <input type="text" class="form-control" id="inputMobileNumber" placeholder="Mobile No." th:field="*{mobileNumber}" />
                                </div>
                                <div class="form-group">
                                    <label for="inputAddress">Address</label>
                                    <input type="text" class="form-control" id="inputAddress" placeholder="Address" th:field="*{address}" />
                                </div>
                                <div class="text-right">
                                    <a href="/contacts" th:href="@{/contacts}" class="btn btn-default">Cancel</a>
                                    <button type="submit" class="btn btn-primary">Save</button>
                                </div>
                            </form>
                        </div>
                        <div class="panel-footer">
                        </div>
                    </div>
                    <!--</div>-->
                </div>
            </div>
        </div>
        <!--/*/ <th:block th:include="fragments/js :: body"></th:block> /*/-->
        <script src="../static/js/app.js" th:src="@{/js/app.js}"></script>
    </body>
</html>

Static Files

CSS

static/css/app.css

body {
  padding-top: 60px;
}

.starter-template {
  padding: 40px 15px;
  text-align: center;
}

Also included are CSS (bootstrap.min.css, bootstrap-theme.min.css) and JavaScript (bootstrap.min) files of Bootstrap as well as jQuery. They can also be downloaded from Bootstrap website.

Running the Application

$ mvn package && java -jar target/springbootcrud-0.0.1.jar

Open your browser and point to: http://localhost:8080

Screenshots


Home Page


Create Project in Spring Initializr

Contacts Page


Contact List (without records)


Create Project in Spring Initializr

Add Contact Form


Create Project in Spring Initializr

Create Project in Spring Initializr

Contact List (with records)


Create Project in Spring Initializr

View Contact


Create Project in Spring Initializr

Edit Contact Form


Create Project in Spring Initializr

Please post your comments or suggestions.

You can download complete source code here.