Lab#SB00-2: CRUD User

Spring Boot controllerView CRUD User (and Librarian)

Spring-Boot
lab
Spring Boot
Author

albertprofe

Published

Tuesday, June 1, 2021

Modified

Saturday, September 7, 2024

📘 Spring Boot Lab#SB00-2: CRUD User (and Librarian)

CRUD stands for Create, Read, Update, and Delete - the four basic operations for persistent storage of data.

In the context of a LibraryMangement, CRUD operations would allow us to create new users, retrieve information about existing users, update user information, and delete users from the system.


We should implement it in several domains.
  • First, we’ll create our User class and annotate it with @Data, @NoArgsConstructor, and @AllArgsConstructor.
  • Next, we’ll create our UserService class and annotate it with @Service. This class will use a HashMap to store user data, and will expose methods to create, read, update, and delete users.
  • Next, we’ll create our UserController class and annotate it with @Controller. This class will handle requests to view, create, update, and delete users. We’ll inject the UserService into the controller using @Autowired.
  • Finally, we’ll create our Thymeleaf HTML templates. We’ll create an index.html or home.html template to display a list of users, a createUser.html template to allow users to create new users, and aneditUser.html template to allow users to edit existing users.

1 Overview

We’ll be using a Java class called User to represent user data.

The @Data annotation is used to generate getters, setters, equals, hashCode, and toString methods for the class. The @NoArgsConstructor and @AllArgsConstructor annotations are used to generate constructors with no arguments and all arguments, respectively.

We’ll also be using a HashMap to store user data, with userId as the key. This will allow us to quickly retrieve user data using the user ID as a lookup key.

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

// generates getters, setters, equals, 
// hashCode, and toString methods
@Data
@NoArgsConstructor // generates a no-args constructor
@AllArgsConstructor // generates a constructor with all arguments
public class User {
    private String userId;
    private String name;
    private String address;
    private int age;
}

We might implement the CRUD operations for our LibraryMangement system:

  • Create: To create a new user, we’ll need to generate a unique user ID and create a new User object with the provided user data. We can then add the new User object to our HashMap using the generated user ID as the key.
  • Read: To retrieve information about an existing user, we’ll need to look up the User object in our HashMap using the user ID as the key.
  • Update: To update user information, we’ll need to retrieve the User object from our HashMap using the user ID as the key, and then update the relevant properties of the User object.
  • Delete: To delete a user from the system, we’ll need to remove the User object from our HashMap using the user ID as the key.

2 References

2.1 Library5

2.1.1 Repos

2.1.2 Java Classes

2.1.3 Templates Thynmeleaf

2.2 LibraryManagement: controllerView

3 controllerView

3.1 folder-tree project & domains

Folder-tree controllerView Project just with Read feature

Folder-tree controllerView Project just with Read feature

3.2 Home

This cycle defines a @Controller class that handles requests to the URL "/home". When a request is made, it adds the current date and time to the model object, and then returns the name of the view to be rendered.

The view will have access to the “todayDate” attribute and can use it to display the current date and time.

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

@Controller
public class HomeController {
    @RequestMapping("/home")
    public String gethome(Model model){

        model.addAttribute("todayDate", new Date().toString());
        return "home";
    }

}

render html chrome: /home

render html chrome: /home

4 CRUD: read

4.1 Users

The cycle request-response relies in the @Controller class called UserController, which maps requests to the "/user/users" URL.

It uses an @Autowired UserService to fetch a list of users and adds them to the model, before returning a view called "user/users".

package com.example.myFirstSpring.controller;

import com.example.myFirstSpring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserService userService;

    @RequestMapping("/users")
    public String getAllUsers(Model model){
        // fetch all users, add to model
        model.addAttribute("users", userService.getAllUsers());
        return "user/users";
    }
}

render html chrome: /user/users

render html chrome: /user/users

Here we will define the @Service class, called UserService with a method called getAllUsers, which returns a HashMap of user objects.

The HashMap is populated with 20 fake user objects using a utility method called populateFakeUsers.

package com.example.myFirstSpring.service;

import com.example.myFirstSpring.model.User;
import com.example.myFirstSpring.utils.Utils;
import org.springframework.stereotype.Service;
import java.util.HashMap;

@Service
public class UserService {
    public static HashMap<String, User> users = new HashMap<>();

    static {
        Utils.populateFakeUsers(20, users);
    }

    public HashMap<String, User> getAllUsers (){

        return users;
    }
}

4.1.1 Code source

4.2 Librarians

Like User request-response cyle read feature (user-case and user-story) we will code the Librarian.

Note

The Librarian cycle request-response would function similarly to the User cycle request-response, but with different URLs, controller methods, and service methods tailored to the Librarian entity.

  1. The LibrarianController would map requests to the appropriate URLs, such as "/librarian/librarians".

  2. These requests would be handled by methods in the LibrarianController, which would call methods from the LibrarianService to fetch or manipulate data.

The LibrarianService would contain methods for fetching and manipulating data from the Librarian entity, similar to the UserService for the User entity.

  1. Templates would also be created for rendering views related to the Librarian entity, using Thymeleaf or a similar templating engine.

4.2.1 Code source

render html chrome: /librarian/librarians

render html chrome: /librarian/librarians

5 CRUD: create

5.1 Create book (reference)

CRUD: create book

CRUD: create book
HTML Form: name/id

In HTML forms, the name and id attributes serve different purposes for form elements:

  • name attribute: This attribute defines the name of the input element, which is used to identify the form data in the request that is sent to the server when the form is submitted. The name attribute is required for all form controls and must be unique within the form.

  • id attribute: This attribute is used to uniquely identify an HTML element. It can be used to target the element with CSS or JavaScript, and can also be used to associate a label with an input element using the for attribute.

HTML Form: action

The action attribute is used to specify the URL of the server-side script or class/method that will process the form data when the form is submitted.

This attribute is required for all forms and specifies the location where the form data will be sent.

The method attribute is also used to specify the HTTP method that will be used to submit the form data, such as GET or POST.

HTML Form: for

The for attribute is used to associate a label with an input element.

The for attribute specifies which input element the label belongs to by referring to the id attribute of the input element.

5.2 Create user

  1. The client sends a request to server-controller by an endpoint to get the create-user form: Users template
<p>
    <a th:href="@{/user/emptyForm}">Add new user to HashMap</a>
</p>

  1. The server-controller method handles the request and sends a response with the create-user form: UserController
@RequestMapping("/emptyForm")
public String sendUserForm(){
    return "user/userForm";
}
  1. The client renders the create-user form received from server-controller: userForm
<form action="/user/createUser" >
    <p>
        <label for="name">Name</label>
        <input  type="text" name="name"  id="name"></p>
    <p>
        <label for="address">address</label>
        <input  type="text" name="address"  id="address"></p>
    <p>
        <label for="age">age</label>
        <input type="number" name="age"  id="age" ></p>
    <p>
        <input type="submit" value="Add new user" />
</form>

  1. The client sends a request to add this new user to HashMap: userForm
<form action="/user/createUser" >
    <input type="submit" value="Add new user" />
</form>

  1. The sever-controller method handles the request, saves the user object in a HashMap and redirects the reponse: UserController
@RequestMapping("/createUser")
public String createUser(User user){
    userService.createUser(user);
    return "redirect:users";
    //return "user/userCreationResult";
}

6 CRUD: update

6.1 Update book (reference)

CRUD: update book

CRUD: update book

6.2 Update user

  1. The client sends a request to server-controller by an endpoint to get the update-user form: Update user template
<td>
     <a th:href=
     "@{packedUserForm(idFromView=${user.value.userId})}"
     >Update</a>
</td>

  1. The server-controller method handles the request and sends a response with the update-user form: UserController
@RequestMapping("/packedUserForm")
public String packedUserForm(@RequestParam("idFromView") String id ,
                                Model model){

    User userFound = userService.findUserById(id);

    if (userFound != null){
        model.addAttribute("userFromController", userFound);
        model.addAttribute("message", "User  found");}
    else
        model.addAttribute("message", "User not found");

    return "user/userToUpdateForm";
}
  1. The client renders the update-user form received from server-controller: userForm
<form th:action=
        "@{updateUser/{idFromView}
        (idFromView=${userFromController.idUser})}"
      th:object=
        "${userFromController}"
      method=
        "post">
    <p>
        <label for="userId">User Id</label>
        <input  type="number" name="userId" id="userId" 
                th:field="*{userId}" readonly></p>
    <p>
    <p>
        <label for="name">Name</label>
        <input  type="text" name="name" id="name"
                th:field="*{name}"></p>
    <p>
        <label for="address">Address</label>
        <input  type="text" name="address" id="address"
                th:field="*{address}"></p>
    <p>
        <label for="Age">Age</label>
        <input type="number" name="age" id="age"
                th:field="*{age}" ></p>
    
        <input type="submit" value="Update user"/>
    </div>
</form>

  1. The client sends a request to update this user to HashMap: userForm
<form th:action=
        "@{updateUser/{idFromView}
        (idFromView=${userFromController.idUser})}"
      th:object=
        "${userFromController}"
      method=
        "post">
</form>

From age 50 to 51

From age 50 to 51
  1. The sever-controller method handles the request, updates the user object in a HashMap and redirects the reponse: UserController and UserService
@PostMapping("/updateUser/{idFromView}")
public String updateUser(@PathVariable("idFromView") String id,
                            User updatedUser) {

    User userFound = userService.findUserById(id);

    if (userFound != null) {
        userService.updateUserByUser(updatedUser);
        return "redirect:/user/users";
    } else return "user/userNotFound";
}

7 CRUD: delete

  1. The client sends a request to server-controller by an endpoint to delete a user: Users template

We are going to delete one user: Antoine

We are going to delete one user: Antoine
  1. The server-controller method handles the request and sends a response with the result. It could be success (user deleted) or fail (the deletion operation could not be done): UserController
@RequestMapping("/deleteUser")
public String deleteUser(@RequestParam("idFromView") String id) {

    User userFound = userService.findUserById(id);

    if (userFound != null) {
        userService.deleteUserById(id);
        return "redirect:/user/users";
    } else return "user/userNotFound";


}

User Antoine deleted

User Antoine deleted

The @Controller uses userService to call @Service: deleteUserById:

public void deleteUserById(String id) {
    users.remove(id);
}
  1. The server-controller method after handled the request sends the response: redirects the reponse to users: UserController redirect