Spring Boot Web Application – Part 4 – Spring MVC

Spring Boot Web Application – Part 4 – Spring MVC

67 Comments

Last Updated on October 21, 2024 by jt

This is the 4th part of my tutorial series on building a web application using Spring Boot.  In the last part of the series, we looked at setting up Spring Data JPA for database persistence. In the second part of the series, we looked at using Thymeleaf for building the web pages. And we started off in the first part looking at using the Spring Initializr to start our Spring Boot project.

In this part of the series, we tie everything together to provide a working Spring Boot web application. An application which will display data from the database, and allow you to create new records, update existing records, and delete selected records too.

Spring MVC

In this part of my tutorial series for Spring Boot, we’re going to look at setting up a Spring MVC controller to support CRUD operations against the database.

MVC stands for Model, View, Controller. The MVC design pattern is probably the most popular design pattern used when writing code to generate dynamic web content. This design pattern is not limited to Java nor Spring. The MVC design pattern has been applied in Javascript, PHP, .NET, Python, and many other programming languages. The MVC pattern is popular because it does a great job of separating concerns, and leads you to a clean, maintainable, and easy to understand code base.

MVC Overview

Model

Model refers to a data model or some type of data structure. For example, a web page showing a list of products, the ‘model’ would contain a list of product data.

View

The view layer, in Java frequently a JSP. This will take data from the Model and render the view.

Controller

I like to describe the controller as a traffic cop. It will take an incoming request, decide what to do with it, then direct the resulting action. For example, the controller could get a view product request. It will direct service to get the product data, then direct to the product view and provide the ‘model’ (product data) to the view.

Single Responsibility Principle Applied to MVC

Frequently when dealing with legacy code, I see a lot of leakage between the layers. JSP pages making database calls. Controllers building database connection pools. On one legacy application, I recently worked with the JSP pages and controllers were littered with static method calls, which ultimately made a call to an Oracle database. Because of this, the application was impossible to run outside of the application server. The code was so tightly coupled, there were no unit tests in what is a very large code case. Why? You can’t run any of the code under JUnit because of all the embedded static method calls.

In an MVC application, each component has a specific function in life. You should be able to unit test your controllers. Using Mocks, you should be able to unit test that your controller returns the proper model, and makes the proper decisions.

CRUD Operations with Spring MVC

CRUD is a common acronym for Create, Read, Update, and Delete. In the last part of the series, we looked at creating a CRUD repository using Spring Data JPA. In this post, we’ll look at setting up the Spring MVC controller for the corresponding CRUD operations. We’ll continue using the Product class we previously used.

Create

The Create operation is a two-step operation. The first step needs to display the create form, the second needs to do the save of the form post.

Here is the controller code for displaying the create product form.

    @RequestMapping("product/new")
    public String newProduct(Model model){
        model.addAttribute("product", new Product());
        return "productform";
    }

The @RequestMapping  annotation maps the url product/new to this controller action. Our controller method is taking in the model attribute. This is the ‘model’ being returned to the view layer.

You can see in the code, we are returning an empty product class to the view. This is more of a trick to re-use the view code for both the Create and Update form. By providing an empty Product object, we reduce the likelihood of null pointer errors when rendering the view. You can either provide an empty object to the model or do a lot of null checking in the view. From experience, I’ve found this simpler.

Our create view is going to have a form post. We need a controller action to handle this.

    @RequestMapping(value = "product", method = RequestMethod.POST)
    public String saveProduct(Product product){
        productService.saveProduct(product);
        return "redirect:/product/" + product.getId();
    }

In this controller method, we’re handing the form post. The @RequestMapping  annotation says to take the ‘url’ product and the HTTP request method of POST to map it to this controller method. You can see how we’re asking for a Product object as input to the controller method. One of the cool things about Spring MVC is that it will take your form parameters and automatically bind them to a Product object. The object is automatically created and passed into your controller method. The Spring Framework saves you from the mundane work of parsing out HTTP request parameters.

You can see how we’re using a product service to handle the persistence. This is just a facade to the Spring Data JPA repository we created in the last post. I’m going to skip over the persistence code here. You’ll be able to find it in github. I want you to notice how I’m writing to an interface. The controller doesn’t know about persistence. It does not need to. Storing data is not the job of the controller. Maybe that method is using JDBC. Maybe it is calling a web service. Maybe it’s using JMS. Might be using AQMP. The controller does not care. The controller code does not need to care. This is a great example of decoupling code. Too often I see legacy code where the controllers are doing way too much.

On the last line of the saveProduct method, you can see I’m returning a string with redirect. This tells Spring after the save action to redirect to the view to show the created item. This example just shows the ‘happy path’ – where everything happens as it should. In a more robust controller you’d have logic not only for the happy path, but to redirect to the create form if validations were to fail.

Read

In Read operations, the client is going to tell you what it wants. In our case, the client will give us an Id value, and we’ll return the corresponding Product.

Read by Id

You can see in our controller method, the Request Mapping is using product with an id value in squigglies. This identifies that portion of the url path as an ‘id; value.

    @RequestMapping("product/{id}")
    public String showProduct(@PathVariable Integer id, Model model){
        model.addAttribute("product", productService.getProductById(id));
        return "productshow";
    }

Now, we’re using a new annotation @Pathvariable to inject the id value from the url path into our controller as the ID variable. Again, we’re accepting the model variable into our controller. We’re asking the product service to get the product, and the result is appended to the model object, which is returned to the view. The controller method returns a string to indicate which view to render.

List All

A common method is also to provide a list view. Normally, you’re going to want to add paging or some type of filter. However, in this example, we just want to show a simple example of listing products from the database.

    @RequestMapping(value = "/products", method = RequestMethod.GET)
    public String list(Model model){
        model.addAttribute("products", productService.listAllProducts());
        return "products";
    }

We’ve mapped this controller method to the url /products. We ask the product service for a list of all products and append it to the model attribute products. The controller method returns the string products to tell Spring MVC to render the products view.

Update

Updates are actions against existing entities. Updates are similar to create actions, where we have two controller actions involved. With a create, we’re showing a form for a new item, while an update is going to be populated with data from an existing item. While this is very similar to the create action, we typically will want a separate controller action to show the edit form to capture the data for the update.a

@RequestMapping("product/edit/{id}")
public String edit(@PathVariable Integer id, Model model){
    model.addAttribute("product", productService.getProductById(id));
    return "productform";
}

The good news, functionally the save and view of the saved item is the same as the create action.

Here is our save method once more:

@RequestMapping(value = "product", method = RequestMethod.POST)
public String saveProduct(Product product){

    productService.saveProduct(product);

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

You can see we’re using Spring to bind the form post parameters to a Product object, then calling the product service to save the item. Then just like in the save method of the create process, we want to view the saved product, so we redirect to the show product view.

Delete

There are a few different ways to implement a delete action. One of the easiest is to use a url with the ID for the delete action. This can then be implemented on the web forms as a simple URL to click on. Below is the controller action for the delete action.

@RequestMapping("product/delete/{id}")
public String delete(@PathVariable Integer id){
    productService.deleteProduct(id);
    return "redirect:/products";
}

This method will take in the id value from the URL and pass it to the delete method of the product service. Since we’re not creating or updating a product, a typical course of action is to return to the list view. In this example, we redirect to the products view to show the user a list of products.

Summary of CRUD Operations

At this point, we’ve covered the necessary controller actions to support CRUD operations on an entity. You can see these operations work in conjunction with the Spring Data JPA methods we looked at in the previous post on Spring Data JPA. I’m using a Facade Service to mask the Spring Data JPA implementation. We’ll take a look at the Facade in the next section.

Spring Facade Service

You can see in the controller methods above, there is no dependency on the persistence layer. The controller is completely unaware of how data is being persisted. This is exactly as it should be. Too often I see legacy code where the controller is interacting with the database directly. This is a very poor coding practice. It makes your code tightly coupled and hard to maintain.

Code to an Interface

When using Spring to develop applications it is always best to develop to an interface, especially when leveraging the benefits of dependency injection.  To support our controller actions, I wrote the following interface.

ProductService.java

package guru.springframework.services;


import guru.springframework.domain.Product;

public interface ProductService {
    Iterable<Product> listAllProducts();

    Product getProductById(Integer id);

    Product saveProduct(Product product);
}

Notice how this interface is rather generic? Can you tell how data is being persisted? JDBC? Spring Data JPA? Web Service? JMS? This is what decoupling is about. At this point, the answer is all of the above. We just need to provide the appropriate implementation.

Spring Data JPA Product Service Implementation

In the last post of this series, we looked at using Spring Data JPA. Now we need an implementation of the Product Service which will use the Spring Data JPA repositories.

Spring Data JPA Repository

We’ll need to inject an instance of the Spring Data JPA repository into the implementation of our product service. You can do so by declaring a property for the repository and annotating the setter method with the @Autowired  annotation.

private ProductRepository productRepository;

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

List Products

Using Spring Data JPA, it becomes trivial to list all the products for our application. While we did not actually create a findAll()  method on the repository we defined, we inherited by extending the CrudRepository in Spring Data JPA. This is one of many handy features of Spring Data JPA. It’s going to provide us an implementation of the findAll()  method, which we do not need to write code for.

@Override
public Iterable <Product> listAllProducts() {
    return productRepository.findAll();
}

Get Product (Read)

To fetch a product by its id value, again, we can leverage a method implemented for us by Spring Data JPA.

@Override
public Product getProductById(Integer id) {
    return productRepository.findOne(id);
}

Save Product (Create / Update)

Spring Data JPA also provides us an implementation of a save method for saving entities. We use this method in creating and updating products in our web application.

@Override
public Product saveProduct(Product product) {
    return productRepository.save(product);
}

Delete Product (Delete)

Finally, in our CRUD operations, Spring Data JPA provides us an implementation of a delete method. Spring Data JPA overloads the delete method, accepting just the ID value, or the entity itself. For our purposes, we are using the ID value to delete the desired entity.

@Override
public void deleteProduct(Integer id) {
    productRepository.delete(id);
}

Summary of Spring Data JPA Usage

In this example, we implemented the CRUD operations using a CrudRepository supplied by Spring Data JPA. If you look at the code you will see all we did was extend the Spring Data JPA CrudRepository to create our Product Repository. We did not define, nor implement additional methods. We’re not declaring transactions. We’re not writing any SQL. I hope you can see the simplicity and time saving using tools like Spring Data JPA can bring you.

Thymeleaf

Thymeleaf Fragments

Thymeleaf fragments are a very powerful feature of Thymeleaf. They allow you to define repeatable chunks of code for your website. Once you define a Thymeleaf fragment, you can reuse it in other Thymeleaf templates. This works great for components you wish to reuse across your web pages.

In developing the Spring Boot Web Application, I found two uses for Thymeleaf templates. The first was common includes of the CSS, Javascript. The second was for a common menu I wanted to display at the top of each web page.

Includes

Below is the Thymeleaf Fragment I’m using for the HTML header includes. You can see its a normal HTML document, using Thymeleaf tags to define the resources for a page.

headerinc.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en" th:fragment="head">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link href="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.4/css/bootstrap.min.css"
          th:href="@{/webjars/bootstrap/3.3.4/css/bootstrap.min.css}"
          rel="stylesheet" media="screen" />

    <script src="http://cdn.jsdelivr.net/webjars/jquery/2.1.4/jquery.min.js"
            th:src="@{/webjars/jquery/2.1.4/jquery.min.js}"></script>

    <link href="../static/css/guru.css"
          th:href="@{/css/guru.css}" rel="stylesheet" media="screen"/>
</head>
<body>

</body>
</html>

Menu

For our Spring Boot Web Application, I chose to use the Bootstrap CSS framework. I’m a big fan of Bootstrap. It’s easy to use, and its components look great. Bootstrap CSS has a menu component which I chose to use for the menu system.

In this Thymeleaf fragment, I’m providing the Bootstrap CSS menu I want to place at the top of all my pages. I also have a section to show my Spring Boot logo on each page.

header.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <link href="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.4/css/bootstrap.min.css"
          th:href="@{/webjars/bootstrap/3.3.4/css/bootstrap.min.css}"
          rel="stylesheet" media="screen"/>

    <script src="http://cdn.jsdelivr.net/webjars/jquery/2.1.4/jquery.min.js"
            th:src="@{/webjars/jquery/2.1.4/jquery.min.js}"></script>

    <link href="../../static/css/guru.css"
          th:href="@{css/guru.css}" rel="stylesheet" media="screen"/>
</head>
<body>

<div class="container">
    <div th:fragment="header">
        <nav class="navbar navbar-default">
            <div class="container-fluid">
                <div class="navbar-header">
                    <a class="navbar-brand" href="#" th:href="@{/}">Home</a>
                    <ul class="nav navbar-nav">
                        <li><a href="#" th:href="@{/products}">Products</a></li>
                        <li><a href="#" th:href="@{/product/new}">Create Product</a></li>
                    </ul>

                </div>
            </div>
        </nav>

        <div class="jumbotron">
            <div class="row text-center">
                <div class="">
                    <h2>Spring Framework Guru</h2>

                    <h3>Spring Boot Web App</h3>
                </div>
            </div>
            <div class="row text-center">
                <img src="../../static/images/NewBannerBOOTS_2.png" width="400"
                     th:src="@{/images/NewBannerBOOTS_2.png}"/>
            </div>
        </div>
    </div>
</div>
</body>
</html>

Including Thymeleaf Fragments

Example

Previously, we defined an index page for our Spring Boot web application. You can apply Thymeleaf templates through the use of HTML comments. By doing this, you preserve the ability of the document to be viewed in the browser. You will be able to see the document okay in your browser, but the fragment portions will be omitted. The fragments are only included when the Thymeleaf template is rendered by Spring.

Remember, Spring will be reading the Thymeleaf templates, then producing output based upon the Thymeleaf directives.

index.html

<!DOCTYPE html>
<html>
<head lang="en">

    <title>Spring Framework Guru</title>

    <!--/*/ <th:block th:include="fragments/headerinc :: head"></th:block> /*/-->
</head>
<body>

<div class="container">
    <!--/*/ <th:block th:include="fragments/header :: header"></th:block> /*/-->
</div>
</body>
</html>

You can see how our index page is very simple now. While this is a very lean HTML document when Spring renders it at run time, you will see HTML looking like this:

Actual HTML Rendered to Browser

<!DOCTYPE html>

<html>
<head lang="en">

    <title>Spring Framework Guru</title>

     
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link href="/webjars/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" media="screen" />

    <script src="/webjars/jquery/2.1.4/jquery.min.js"></script>

    <link href="/css/guru.css" rel="stylesheet" media="screen" />
 
</head>
<body>

<div class="container">
     
        <nav class="navbar navbar-default">
            <div class="container-fluid">
                <div class="navbar-header">
                    <a class="navbar-brand" href="/">Home</a>
                    <ul class="nav navbar-nav">
                        <li><a href="/products">Products</a></li>
                        <li><a href="/product/new">Create Product</a></li>
                    </ul>

                </div>
            </div>
        </nav>

        <div class="jumbotron">
            <div class="row text-center">
                <div class="">
                    <h2>Spring Framework Guru</h2>

                    <h3>Spring Boot Web App</h3>
                </div>
            </div>
            <div class="row text-center">
                <img src="/images/NewBannerBOOTS_2.png" width="400" />
            </div>
        </div>
     
</div>
</body>
</html>

Notice how Thymeleaf and Spring have merged the contents of the index.html document and the two Thymeleaf fragment documents? Now you have pure HTML, and Thymeleaf tags are not rendered to the HTML content sent to the browser.

The index.html Thymeleaf template will show this page in your browser.

Thymeleaf Index page

Thymeleaf Views for CRUD Application

Show Product

Showing a product is one of the simpler operations under Spring MVC and Thymeleaf. Our controller returned a product object to the model and bound it to the property product.  Now we can use the typical name-dot-property syntax to access properties of the product object.

This Thymeleaf tag:

<p class="form-control-static" th:text="${product.id}">Product Id</p></div>

Will get a text from the description property of the product object and replace the description text in the paragraph HTML tag.

Here is the full Thymeleaf template for showing a product:

productshow.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">

    <title>Spring Framework Guru</title>

    <!--/*/ <th:block th:include="fragments/headerinc :: head"></th:block> /*/-->
</head>
<body>
<div class="container">
    <!--/*/ <th:block th:include="fragments/header :: header"></th:block> /*/-->

    <h2>Product Details</h2>
        <div>
            <form class="form-horizontal">
                <div class="form-group">
                    <label class="col-sm-2 control-label">Product Id:</label>
                    <div class="col-sm-10">
                        <p class="form-control-static" th:text="${product.id}">Product Id</p></div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">Description:</label>
                    <div class="col-sm-10">
                        <p class="form-control-static" th:text="${product.description}">description</p>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">Price:</label>
                    <div class="col-sm-10">
                        <p class="form-control-static" th:text="${product.price}">Priceaddd</p>
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-sm-2 control-label">Image Url:</label>
                    <div class="col-sm-10">
                        <p class="form-control-static" th:text="${product.imageUrl}">url....</p>
                    </div>
                </div>
            </form>
    </div>
</div>

</body>
</html>

The shown product Thymeleaf template will show this page:

Thymeleaf Show Product Page

List Products

The list view is a little trickier because now we have a list of products to iterate over. Luckily, Thymeleaf makes this very easy to do.

Here is a snippet showing how to iterate over a list of products.

            <tr th:each="product : ${products}">
                <td th:text="${product.id}"><a href="/product/${product.id}">Id</a></td>
                <td th:text="${product.productId}">Product Id</td>
                <td th:text="${product.description}">descirption</td>
                <td th:text="${product.price}">price</td>
                <td><a th:href="${'/product/' + product.id}">View</a></td>
                <td><a th:href="${'/product/edit/' + product.id}">Edit</a></td>
                <td><a th:href="${'/product/delete/' + product.id}">Delete</a></td>
            </tr>

You can see the syntax of this Thymeleaf tag is similar to a for-each loop in Java.

<tr th:each="product : ${products}">

Our controller added a list of products to the products property to the model, which we pass to the Thymeleaf tag. The variable name we are assigning to the iterator is product.

The body of each tag will be rendered once for each product in the list of products.

Here is the complete Thymeleaf template used for showing a list of products.

products.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">

    <title>Spring Framework Guru</title>

    <!--/*/ <th:block th:include="fragments/headerinc :: head"></th:block> /*/-->
</head>
<body>
<div class="container">
    <!--/*/ <th:block th:include="fragments/header :: header"></th:block> /*/-->
    <div th:if="${not #lists.isEmpty(products)}">
        <h2>Product List</h2>
        <table class="table table-striped">
            <tr>
                <th>Id</th>
                <th>Product Id</th>
                <th>Description</th>
                <th>Price</th>
                <th>View</th>
                <th>Edit</th>
            </tr>
            <tr th:each="product : ${products}">
                <td th:text="${product.id}"><a href="/product/${product.id}">Id</a></td>
                <td th:text="${product.productId}">Product Id</td>
                <td th:text="${product.description}">descirption</td>
                <td th:text="${product.price}">price</td>
                <td><a th:href="${ '/product/' + product.id}">View</a></td>
                <td><a th:href="${'/product/edit/' + product.id}">Edit</a></td>
            </tr>
        </table>

    </div>
</div>

</body>
</html>

Here is the Thymeleaf list products page:

Thymeleaf List Products

Create / Update Product

We can use the same HTML form for creating and updating products. A little trick is to have your controller method return an empty object to the view for the create option and the existing object for the update option. By doing this you don’t need to worry about null objects on the view layer. For a new object, the null properties show up blank. For existing objects, non-null properties will get populated into the form fields.

The following line sets up the form in Thymeleaf.

<form class="form-horizontal" th:object="${product}" th:action="@{/product}" method="post">

The th:object tag binds the product object to the form. Thus, you only use the property names on the form fields. No need to qualify the object name too.

The th:action tag maps the form action to the /product url. And we specify to use the HTML post action for the form.

Here is the controller action this maps back to:

@RequestMapping(value = "product", method = RequestMethod.POST)
public String saveProduct(Product product){
    productService.saveProduct(product);
    return "redirect:/product/" + product.getId();
}

Notice how we’ve assigned the url product and method POST in the request mapping.

This next step is critical for your updates to work properly. All entities have an ID value. This is not accessible for the user to edit, but it still needs to be included to the post back to the server, so Spring / Hibernate can find the correct entity to update. If this is missing, there is no way to distinguish between an update and a create. If the ID property is missing from the form post, Spring Data JPA will think it’s a new item and create a new entity.

The way to handle this is through the use of hidden form fields. In this snippet, we’re assigning hidden fields for the Id and version values. (A best practice in Hibernate is to use a version property to detect conflicting updates.)

<input type="hidden" th:field="*{id}"/>
<input type="hidden" th:field="*{version}"/>

Here is the complete product form.

productform.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">

    <title>Spring Framework Guru</title>

    <!--/*/ <th:block th:include="fragments/headerinc :: head"></th:block> /*/-->
</head>
<body>
<div class="container">
    <!--/*/ <th:block th:include="fragments/header :: header"></th:block> /*/-->

    <h2>Product Details</h2>
    <div>
        <form class="form-horizontal" th:object="${product}" th:action="@{/product}" method="post">
            <input type="hidden" th:field="*{id}"/>
            <input type="hidden" th:field="*{version}"/>
            <div class="form-group">
                <label class="col-sm-2 control-label">Product Id:</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" th:field="*{productId}"/>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-2 control-label">Description:</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" th:field="*{description}"/>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-2 control-label">Price:</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" th:field="*{price}"/>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-2 control-label">Image Url:</label>
                <div class="col-sm-10">
                    <input type="text" class="form-control" th:field="*{imageUrl}"/>
                </div>
            </div>
            <div class="row">
                <button type="submit" class="btn btn-default">Submit</button>
            </div>
        </form>
    </div>
</div>

</body>
</html>

Here is the Thymeleaf product form.

Thymeleaf Product Form

Conclusion

In this post, we built upon the previous posts in this series on building a web application using Spring Boot to have a functional web application which performs CRUD operations against a single entity. At this point, you can check out the project from Github and build it using Maven. Spring Boot will create an executable JAR, which you can run to demo the application. Spring Boot will run the application in an embedded Apache Tomcat instance and you will be able to see the application running at http://localhost:8080.

In the next part of this series, I’ll show you how to secure content using Spring Security.

Get The Source!

Like all of my tutorials, the source code for this post is available on GitHub here.

Save

About jt

    You May Also Like

    67 comments on “Spring Boot Web Application – Part 4 – Spring MVC

    1. September 14, 2015 at 1:31 am

      Hello,Springframework Guru,Really thank you for the wonderful tutorials.
      I was searching for such a tutorials with tutor having real world programming experience and i guess you are here.
      Thanks alot.

      Reply
      • September 14, 2015 at 8:41 am

        Thanks!!

        Reply
        • September 14, 2015 at 10:43 pm

          Are There any Tutorials along with Spring Security with jpa for examples Simple Login using mysql with multiple user roles coming up in near future..
          Hoping for it.. 🙂

          Reply
          • September 15, 2015 at 7:16 am

            Yes – I’m planning to do something with Spring Security next in this series.

            Reply
            • September 15, 2015 at 10:57 pm

              cool.You surely gonna rock it

    2. September 14, 2015 at 3:06 pm

      Big thanks for another good tutorial! Keep on going 🙂

      I have one question too – why are fragments commented out from code, and how they work like that?
      For example, in index.html the commented out fragments work, and just plain
      will work too.
      Intellij will only show errors though, because it wont understand tags when fragment without xmlns declaration.

      Thanks alot

      Reply
      • September 15, 2015 at 7:18 am

        The fragments are referenced as html comments so browsers will ignore them when viewing the HTML directly. But are picked up by Thymeleaf when rendered.

        Reply
    3. September 14, 2015 at 3:08 pm

      (th:block th:include=”fragments/headerinc :: head”)(/th:block)
      replaced < with simple parenthesis 🙂

      Reply
    4. September 15, 2015 at 8:34 am

      Nice one!

      Reply
    5. September 21, 2015 at 9:16 am

      Do you have a good guide to make it works with Angular JS ?
      i got my Front-end in Angular JS consuming the Web Service RestFUL (Spring Boot / MVC / Hibernate).
      From this, i would be glad to make my CRUD works.

      Reply
      • September 21, 2015 at 9:56 am

        Using @RestController instead of @Controller. I guess it’s not that simple to make something as generic as you previous example.

        Reply
    6. September 21, 2015 at 9:26 am

      No I don’t. I’m using Angular JS and Grails for client project. Setting up the rest stuff under Spring MVC instead of Grails would be fairly trivial. Maybe I’ll do a post on that in the future.

      Reply
    7. September 30, 2015 at 3:23 am

      I think you are a angel

      Reply
    8. September 30, 2015 at 11:01 am

      Really awesome!

      Especially the trick with the H2 Console: magic. It’s really helpful.
      Thank you so much and keep on going.

      Meziano

      Reply
    9. October 1, 2015 at 10:27 am

      Hello jt,
      just a little remark: in productform.html I miss
      Without this, when editing a product, we lose the value of productId.

      In the ProductServiceImpl we may set the productId before saving the “new” product:
      @Override
      public Product saveProduct(final Product product) {
      if (product.getProductId() == null || product.getProductId().trim().isEmpty()) {
      product.setProductId(…);
      }
      return productRepository.save(product);
      }

      Thank you again!
      Meziane

      Reply
      • July 18, 2016 at 7:54 am

        Meziane, I think the real problem was a section missing in the productform.html:

        Product Id:

        Reply
      • August 22, 2016 at 11:33 am

        Meziane, what value are you sending to the product.setProductId()

        Reply
    10. October 1, 2015 at 11:45 am

      Hello nice tutorial!!! It helps me a lot. Could you write about spring boot and restull api? Thanks

      Reply
    11. October 2, 2015 at 2:50 am

      Are There any Tutorials along with Spring Security with jpa for examples Simple Login using mongodb with multiple user roles coming up in near future..
      Hoping so..thanks

      Reply
    12. October 31, 2015 at 11:43 pm

      Tremendous post, I’m from Peru and I was looking for a post that uses Spring MVC and Thymeleaf.
      I found a problem in your products.html, you write this:

      View

      But for to have a relative path, you need to use:

      View

      Sorry for my bad english :p

      Reply
      • October 31, 2015 at 11:49 pm

        Sorry …the html code that I attached wasn’t posted.

        th:href=”${‘/product/’ + product.id}”>View

        Relative path:
        th:href=”@{‘/product/’ + ${product.id}}”>View

        Reply
        • August 22, 2016 at 11:35 am

          Arthur, could you please post the whole section? Thanks.

          Reply
    13. November 20, 2015 at 11:21 am

      Muchas gracias, excelente tutorial sobre spring!

      Reply
      • November 20, 2015 at 11:34 am

        Thanks!!

        Reply
    14. November 28, 2015 at 11:46 am

      Really excellent tutorial. I have a question though. While everything is working fine and the pages are displaying properly, I am getting the following message in my console window in Eclipse Tool Suite :
      2015-11-28 16:44:28.741 ERROR 10221 — [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

      java.lang.NullPointerException: null
      at java.io.FilterInputStream.read(FilterInputStream.java:133)
      at java.io.FilterInputStream.read(FilterInputStream.java:107)
      at org.springframework.util.StreamUtils.copy(StreamUtils.java:123)

      Reply
      • November 28, 2015 at 6:00 pm

        Not sure what the cause is. I can’t recreate with my code.

        Reply
    15. November 28, 2015 at 11:59 am

      Also, on the productshow.html is it necessary to have the tag ?

      Reply
    16. November 28, 2015 at 1:19 pm

      the from tag i mean

      Reply
      • November 28, 2015 at 6:00 pm

        sorry, I don’t understand the question

        Reply
    17. December 19, 2015 at 4:19 am

      Great tutorial!

      I have a question regarding the update functionality. When I edit a product and click the Submit Button a new product will be created, instead of the old one being replaced. How can I change this? What should I do?

      Reply
      • December 19, 2015 at 7:16 am

        Thanks! Verify you have the id and version as hidden properties in the form for the submit. Sounds like Hibernate can’t find the existing object.

        Reply
        • December 19, 2015 at 10:57 am

          Thanks for the reply.
          But with the tutorial code the update functionality isn’t implemented, is it? You would have to set the attributes of the product and then save it. Or am I missing something?
          Where in the code is it written that by clicking “Create” (after clicking on edit and changing some text), the entity should be replaced and not a new one created?

          Reply
          • December 21, 2015 at 4:12 pm

            HIbernate uses the ID property to determine if the entity is new or already persisted. A new entity will have a null id.

            Reply
    18. December 27, 2015 at 6:05 am

      Thanks for this great guide! I have a question about the menu, I got menu as below, can you tell me how to solve it?
      http://www.brickshelf.com/gallery/mikezang/java/screen_shot_2015-12-27_at_20.15.08.jpg

      Reply
      • December 28, 2015 at 7:34 am

        Thanks – looks like your CSS isn’t resolving.

        Reply
        • December 28, 2015 at 10:18 am

          I found reason because I import the newest jar in pom.xml so that they are not the same version as in header.html.
          By the way, I found another problem when I try to use MySQL, I checked with your source, they don’t have difference, especially for files pom.xml, application.properties and so on, but my project show warning as below, though I can run project without problems, do you know why?

          WARN: Establishing SSL connection without server’s identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn’t set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to ‘false’. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.

          Reply
    19. December 28, 2015 at 8:45 am

      I am writing a web application using thymeleaf, springboot using your demo. I have one model class with id, name, address, state and district field, state and district are list of string data. I have 3 tables user table, state table with id, state_name, state_code, and district table with id, district_code, district_name, state_code.

      Q1. When I load my newrecord view it should fatch all record from state table and populate in select element?.

      Q2. When I select state from select fatch all dstrict list of that state?.

      Q3. When I open same record in edit mode state and district list show its default value?.

      Reply
    20. December 31, 2015 at 12:10 pm

      Great series of articles. Got a stubbed out web app running in about an hour. One thing I would mention is that you didn’t specify where to put the fragments html files. You need to create a directory called fragments under the templates directory and put them in there. Took mea fe minutes to figure that one out

      Reply
    21. January 2, 2016 at 2:02 am

      Excellent tutorial- thank you!

      One minor correction (of a typo): In the “Show Product” paragraph, although the text is talking about the product description, the code sample is for product.id.

      Thank you again.

      Reply
    22. January 7, 2016 at 8:04 am

      Great tutorial thanks.
      I got lot knowledge using this.
      Is there any thing on Spring Security with mysql or any DB for examples Simple Login role base application using mysql with multiple user roles like admin or user.

      Reply
      • January 7, 2016 at 8:23 am

        I don’t have there here (yet, at least), but I did add Spring Security with DB authentication to my Spring Core course. I just recorded the module yesterday, and will be publishing it to the course in the next day or so.

        Reply
    23. January 7, 2016 at 9:16 am

      Thanks for your great effort.
      How can I get this video?.

      Reply
    24. January 7, 2016 at 9:24 am

      Enroll in my Spring Core course – http://courses.springframework.guru/courses/spring-core

      Reply
    25. January 11, 2016 at 11:06 pm

      thanks,, it’s really helpful,,!

      Reply
    26. March 12, 2016 at 2:36 pm

      Where can i get the css stylesheet?

      Reply
    27. March 28, 2016 at 10:03 pm

      Hi,

      Is it possible to get a good tutorial (like yours) on Spring boot sessions using custom entities instead of the default User to login?

      Thank you very much for the useful tutorials.

      Reply
      • March 29, 2016 at 10:23 am

        Yes, I hope to publish that in the near future.

        Reply
    28. August 22, 2016 at 11:42 am

      John,
      The id gets lost in the update

      Reply
    29. November 10, 2016 at 12:20 pm

      Very good tutorial, thanks for sharing!

      Reply
    30. December 22, 2016 at 5:00 pm

      How much we have to alter if we are not using JPA or h2db, instead we are using Mongdb? I followed your tutorial, My update and delete functions does not work.

      Reply
    31. February 15, 2017 at 11:19 pm

      thanks for your insightful material!
      I am confused by the syntax of:
      <!–/*/ /*/–>
      what is the differences with
      ?

      Cheers.

      Reply
      • February 16, 2017 at 8:58 am

        That’s a Thymeleaf comment syntax to get the fragment included when rendered, but to have it ignored when viewed directly in the browser.

        Reply
    32. April 15, 2017 at 1:16 pm

      Great tutorial, honestly I learned a lot from your blog

      One question regarding controllers above – is it some “convention over configuration” that you don’t specify http methods (for delete method = RequestMethod.DELETE / also for edit (PUT or POST) ???

      Reply
      • April 17, 2017 at 7:47 am

        No – If you don’t specify a request method, then the controller will pickup any method.

        Reply
        • April 17, 2017 at 5:00 pm

          does it mean that we (or someone else with some despicable intentions …) can simply paste the url link ../product/delete/{and id number} to delete the record (sort of sql injection…). Should’t be made with http delete method where request and id are separated? Please advise

          Reply
          • April 17, 2017 at 5:42 pm

            Well of course… But the HTTP method isn’t going to change any of that. This isn’t SQL injection at all. Its a simple API call. The ID value is a parameter of the API call. Securing the API call is a whole different subject & the realm of Spring Security.

            Reply
    33. April 17, 2017 at 12:54 pm

      A very helpful tutorial! I was wondering about JPA and extension… If Hibernate automatically makes these entities, how would you handle if you wanted to make: “class Group” (has id/title/description) and “class SubGroup extends Group” (with its own id/title/description)?

      Basically there can be many SubGroups for every one Group. Is there a way to easily accomplish this?

      Reply
    34. May 12, 2017 at 5:18 am

      Very helpful .Thanks for Posting . Keep it up and all the very best 🙂

      Reply
    35. August 22, 2017 at 1:00 pm

      Thanks Guru for your tutorial, it’s a good help.
      But, I have one question :
      When I try to add some product by my form, system signal me this error :
      “There was an unexpected error (type=Method Not Allowed, status=405).
      Request method ‘GET’ not supported”.

      Can you help to resolve this problem ? Thanks !

      Reply
      • August 22, 2017 at 1:06 pm

        Check the post method in your HTML

        Reply
    36. September 19, 2017 at 4:59 am

      Thank you so much Guru for this tutorial. However, after following all the four series in this tutorial diligently and ran the code, the only thing that the browser presented me with is just a white website that displays index. I also copied and pasted the code to strat again and to my amazement, the result was the same.

      Is there anything am doing wrong or that I need to do to tomcat so that it site is displayed.

      Regards

      Reply
      • September 19, 2017 at 7:31 am

        Hi Samuel,

        Yes, you have something wrong. Hard to say what from what you’ve shared. Check the console for error messages. I suspect you’re getting an exception.

        Reply
        • September 19, 2017 at 8:57 am

          Thank you so much JT for your response. Yes I got the system working. But somehow, the ProductLoader is not getting called. Hence the system is not inserting the data and only the H2 Console is getting displayed. Others are not. Besides there are some configuration that I need to do before the system could work. For instance the security configuration does not work out of the box. I can do a short video and sent it to you, although, I am moving on

          Reply
    37. January 6, 2018 at 11:20 pm

      Update ProductServiceImpl:

      @Override
      public Product getProductById(Integer id) {
      Optional product = productRepository.findById(id);
      if (product.isPresent()) {
      return product.get() ;
      }
      return null ;
      }

      @Override
      public void deleteProduct(Integer id) {
      productRepository.deleteById(id);
      }

      Reply
      • March 16, 2019 at 9:10 am

        Thanks for that!!

        Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

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