Create CRUD apps with Spring Boot 2 + Thymeleaf + MyBatis

Development environment

Construction of development environment

--The following items need to be installed in advance. - Java11 --IDE (Here we will explain in Intellij. Other IDEs are also possible.)

Creating a project

--From Intellij, go to File NewProject and select Spring Initilizr.

image.png

--Select Java 11. (Maybe Java 8 is fine)

image.png

--Check DevTools, Web, Thymeleaf, H2, MyBatis.

image.png

--Enter the project name and you're done.

image.png

Hot deploy settings 1/2

--In order to improve development efficiency, set hot deploy to build automatically when the source is changed. --If there are major changes or the changes are not reflected, you should stop the server → start it. --From Intellij settings, from Build, Run, Deploy Compiler, check □ Build project automatically.

image.png

Hot deploy settings 2/2

--Enter ctrl + shift + a Registry and click Registry .... (If you haven't translated Intellij into Japanese, type registry and clickRegistry ...)

image.png

--compiler. ... .running Check ☑.

image.png

Modify pom.xml

--If you want to use the layout function of tymeleaf, put thymeleaf-layout-dialect in<dependencies> </ dependencies>of pom.xml as follows. (Here, we will proceed on the assumption that it will be included.) --Incorporate Bootstrap and JQuery to improve the look of your design. --Right-click pom.xml → Re-import.

pom.xml


    <dependency>
      <groupId>nz.net.ultraq.thymeleaf</groupId>
      <artifactId>thymeleaf-layout-dialect</artifactId>
      <version>2.3.0</version>
    </dependency>
    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>jquery</artifactId>
      <version>1.11.1</version>
    </dependency>
    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>bootstrap</artifactId>
      <version>3.3.7-1</version>
    </dependency>

Create schema.sql, data.sql

--If you create schema.sql and data.sql, test data will be automatically input to DB at the start of project execution. --Create under the project name / src / main / resources.

schema.sql


CREATE TABLE IF NOT EXISTS user (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name varchar (50) NOT NULL
);

data.sql


INSERT INTO user (name) VALUES ('tanaka');
INSERT INTO user (name) VALUES ('suzuki');
INSERT INTO user (name) VALUES ('sato');

entity creation

--Create an entity package and create a User class under it. --Create a class to hold User data. No annotations needed to use MyBatis. --Add required input validation to name.

java:com.example.demo.entity.User.java


package com.example.demo.entity;


import org.hibernate.validator.constraints.NotBlank;

public class User {

  private int id;

  @NotBlank(message = "{require_check}")
  private String name;

  public int getId() {
    return id;
  }

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

  public String getName() {
    return name;
  }

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

}

Create mapper.java, mapper.xml

--Create a mapper package and create a UserMapper interface under it. --Add @Mapper to the annotation. If this is left as it is, Autowired cannot be done, so add @Repository as well.

java:src/main/java/com.example.demo.mapper.UserMapper.java


package com.example.demo.mapper;

import com.example.demo.entity.User;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface UserMapper {

  List<User> selectAll();

  User select(int id);

  int insert(User user);

  int update(User user);

  int delete(int id);
}

--The mapping file in XML will be read automatically if you create it in the same directory hierarchy as Java under resouces. --namespace: package name in (java) above --id: Method name specified in (java) above --resultType: Return type

src/main/resouces/com/example/demo/mapper/UserMapper


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <select id="selectAll" resultType="com.example.demo.entity.User">
        select * from user
    </select>
    <select id="select" resultType="com.example.demo.entity.User">
        select * from user where id = #{id}
    </select>
    <insert id="insert">
        insert into user (name) values (#{name})
    </insert>
    <update id="update">
        update user set name = #{name} where id = #{id}
    </update>
    <delete id="delete">
        delete from user where id = #{id}
    </delete>
</mapper>

Create controller

--Add 7 actions.

HTTP method URL controller method Description
GET /users getUsers() Display of list screen
GET /users/1 getUser() Display details screen
GET /users/new getUserNew() Display of new creation screen
POST /users postUserCreate() Insertion process of new screen
GET /users/1/edit getUserEdit() Display of edit screen
PUT /users/1 putUserEdit() Edit screen update process
DELETE /users/1 deleteUser() Delete process

java:com.example.demo.controller.UserController


package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import java.util.List;
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.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;

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

  private final UserMapper userMapper;

  @Autowired
  public UserController(UserMapper userMapper) {
    this.userMapper = userMapper;
  }

  /**
   *Display of list screen
   */
  @GetMapping
  public String getUsers(Model model) {
    List<User> users = userMapper.selectAll();
    model.addAttribute("users", users);
    return "users/list";
  }

  /**
   *Display details screen
   */
  @GetMapping("{id}")
  public String getUser(@PathVariable int id, Model model) {
    User user = userMapper.select(id);
    model.addAttribute("user", user);
    return "users/show";
  }

  /**
   *Display of new creation screen
   */
  @GetMapping("new")
  public String getUserNew(Model model) {
    User user = new User();
    model.addAttribute("user", user);
    return "users/new";
  }

  /**
   *Insertion process of new screen
   */
  @PostMapping
  public String postUserCreate(@ModelAttribute @Valid User user,
      BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
      return "users/new";
    }
    userMapper.insert(user);
    return "redirect:/users";
  }

  /**
   *Display of edit screen
   */
  @GetMapping("{id}/edit")
  public String getUserEdit(@PathVariable int id, Model model) {
    User user = userMapper.select(id);
    model.addAttribute("user", user);
    return "users/edit";
  }

  /**
   *Edit screen update process
   */
  @PutMapping("{id}")
  public String putUserEdit(@PathVariable int id, @ModelAttribute @Valid User user,
      BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
      return "users/edit";
    }
    user.setId(id);
    userMapper.update(user);
    return "redirect:/users";
  }

  /**
   *Delete process
   */
  @DeleteMapping("{id}")
  public String deleteUser(@PathVariable int id, Model model) {
    userMapper.delete(id);
    return "redirect:/users";
  }
}

Validation message settings (messages.properties, WebConfig.java created)

--Create messages.properties to set validation messages.

src/main/resources/messages.properties


name=username
require_check={0}Is a required entry

--The magic of creating the following configuration Java file to use the above property file.

java:com.example.demo/WebConfig.java


package com.example.demo;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class WebConfig {

  @Bean
  public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource bean = new ReloadableResourceBundleMessageSource();
    bean.setBasename("classpath:messages");
    bean.setDefaultEncoding("UTF-8");
    return bean;
  }

  @Bean
  public LocalValidatorFactoryBean localValidatorFactoryBean() {
    LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
    localValidatorFactoryBean.setValidationMessageSource(messageSource());
    return localValidatorFactoryBean;
  }
}

Screen creation (layout, show, list, new, edit, form, css creation)

Common layout (layout.html)

--Contents are displayed in <div layout: fragment =" contents "> </ div>.

src/main/resources/templates/commons/layout.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" >

<head>
    <meta charset="UTF-8">
    <link th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}" rel="stylesheet"/>
    <script th:src="@{/webjars/jquery/1.11.1/jquery.min.js}"></script>
    <script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script>
    <link th:href="@{/css/home.css}" rel="stylesheet"/>
    <title>Home</title>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fluid">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">SpringMyBatisDemo</a>
        </div>
    </div>
</nav>

<div class="container-fluid">
    <div class="row">
        <div class="col-sm-2 sidebar">
            <ul class="nav nav-pills nav-stacked">
                <li role="presentation"><a th:href="@{/users}">User management</a></li>
            </ul>
        </div>
    </div>
</div>

<div class="container-fluid">
    <div class="row">
        <div class="col-sm-10 col-sm-offset-2 main">
            <div layout:fragment="contents">

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

</body>
</html>

List screen (list.html)

--Layout is used with layout: decorate =" ~ {commons / layout} ". --The part enclosed by <div layout: fragment =" contents "> </ div> is displayed in the layout.

src/main/resources/templates/users/list.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{commons/layout}">

<head>
  <meta charset="UTF-8">
  <link th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}" rel="stylesheet"/>
  <script th:src="@{/webjars/jquery/1.11.1/jquery.min.js}"></script>
  <script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script>
  <link th:href="@{/css/home.css}" rel="stylesheet"/>
  <title>Home</title>
</head>
<body>
<div layout:fragment="contents">
  <div class="page-header">
    <h1>User list</h1>
  </div>
  <table class="table table-bordered table-hover table-striped">
    <tr>
      <th class="info col-sm-2">User ID</th>
      <th class="info col-sm-2">username</th>
      <th class="info col-sm-2"></th>
    </tr>
    <tr th:each="user:${users}">
      <td th:text="${user.id}"></td>
      <td th:text="${user.name}"></td>
      <td>
        <a class="btn btn-primary" th:href="@{'/users/' + ${user.id}}">Details</a>
        <a class="btn btn-primary" th:href="@{'/users/' + ${user.id} + '/edit'}">Edit</a>
      </td>
    </tr>
  </table>
  <label class="text-info" th:text="${result}">Result display</label><br/>
  <a class="btn btn-primary" th:href="${'/users/new'}">Create New</a>
</div>

</body>
</html>

Details screen (show.html)

--Layout is used with layout: decorate =" ~ {commons / layout} ". --The part enclosed by <div layout: fragment =" contents "> </ div> is displayed in the layout. --The common form is displayed in <div th: include =" users / form :: form_contents "> </ div>.

src/main/resources/templates/users/show.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{commons/layout}">

<head>
  <meta charset="UTF-8"/>
  <title></title>
</head>
<body>
<div layout:fragment="contents">
  <div class="row">
    <div class="col-sm-5">
      <div class="page-header">
        <div>
          <h1>User details</h1>
        </div>
          <table class="table table-bordered table-hover">
            <tr>
              <th class="active col-sm-3">username</th>
              <td>
                <div class="form-group" th:object="${user}">
                  <label class="media-body" th:text="*{name}">
                  </label>
                </div>
              </td>
            </tr>
          </table>
        <form th:action="@{/users}" th:method="get">
          <button class="btn btn-primary btn-lg pull-right" type="submit">List</button>
        </form>
      </div>
    </div>
  </div>
</div>
</body>
</html>

Edit screen (edit.html)

--Layout is used with layout: decorate =" ~ {commons / layout} ". --The part enclosed by <div layout: fragment =" contents "> </ div> is displayed in the layout. --The common form is displayed in <div th: include =" users / form :: form_contents "> </ div>.

src/main/resources/templates/users/edit.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{commons/layout}">

<head>
  <meta charset="UTF-8"/>
  <title></title>
</head>
<body>
<div layout:fragment="contents">
  <div class="row">
    <div class="col-sm-5">
      <div class="page-header">
        <div>
          <h1>User details</h1>
        </div>
        <form th:action="@{/users/{id}(id=*{id})}" th:method="put" th:object="${user}">

          <div th:include="users/form :: form_contents"></div>

          <button class="btn btn-primary btn-lg pull-right" type="submit">update</button>
        </form>
        <form th:action="@{/users/{id}(id=*{id})}" th:method="delete" th:object="${user}">
          <button class="btn btn-danger btn-lg" type="submit">Delete</button>
        </form>
      </div>
    </div>
  </div>
</div>
</body>
</html>

New screen (new.html)

--Layout is used with layout: decorate =" ~ {commons / layout} ". --The part enclosed by <div layout: fragment =" contents "> </ div> is displayed in the layout. --The common form is displayed in <div th: include =" users / form :: form_contents "> </ div>.

src/main/resources/templates/users/new.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{commons/layout}">

<head>
  <meta charset="UTF-8"/>
  <title></title>
</head>
<body>
<div layout:fragment="contents">
  <div class="row">
    <div class="col-sm-5">
      <div class="page-header">
        <div>
          <h1>Create new user</h1>
        </div>
        <form method="post" th:action="@{/users}" th:object="${user}">

          <div th:include="users/form :: form_contents"></div>

          <button class="btn btn-primary btn-lg pull-right" type="submit" name="update">Create</button>
        </form>
      </div>
    </div>
  </div>
</div>
</body>
</html>

Common form

--The common form is inside <div th: fragment =" form_contents "> </ div> and can be embedded with <div th: include =" users / form :: form_contents "> </ div>.

src/main/resources/templates/users/form.html


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

<head>
  <meta charset="UTF-8"/>
  <title></title>
</head>
<body>
<div th:fragment="form_contents">
  <table class="table table-bordered table-hover">
    <tr>
      <th class="active col-sm-3">username</th>
      <td>
        <div class="form-group" th:classappend="${#fields.hasErrors('name')} ? 'has-error'">
          <label>
            <input type="text" class="form-control" th:field="*{name}"/>
          </label>
          <span class="text-danger" th:if="${#fields.hasErrors('name')}"
                th:errors="*{name}">Name error</span>
        </div>
      </td>
    </tr>
  </table>
</div>
</body>
</html>

css

src/main/resources/static/css/home.css


body {
    padding-top: 50px;
}

.sidebar {
    position: fixed;
    display: block;
    top: 50px;
    bottom: 0;
    background-color: #F4F5F7;
}

.main {
    padding-top: 50px;
    padding-left: 20px;
    position: fixed;
    display: block;
    top: 0;
    bottom: 0;
}

.page-header {
    margin-top: 0;
}

--When you open http: // localhost: 8080 / users, the following list screen will be displayed. --List screen

image.png

image.png

--Not input error display

image.png

--Reference -[Spring Dismantling New Book](https://www.amazon.co.jp/%E3%80%90%E5%BE%8C%E6%82%94%E3%81%97%E3%81%AA% E3% 81% 84% E3% 81% 9F% E3% 82% 81% E3% 81% AE% E5% 85% A5% E9% 96% 80% E6% 9B% B8% E3% 80% 91Spring% E8% A7% A3% E4% BD% 93% E6% 96% B0% E6% 9B% B8-Boot2% E3% 81% A7% E5% AE% 9F% E9% 9A% 9B% E3% 81% AB% E4% BD% 9C% E3% 81% A3% E3% 81% A6% E5% AD% A6% E3% 81% B9% E3% 82% 8B% EF% BC% 81Spring-Security% E3% 80% 81Spring-JDBC% E3% 80% 81Spring-MyBatis% E3% 81% AA% E3% 81% A9% E5% A4% 9A% E6% 95% B0% E8% A7% A3% E8% AA% AC% EF% BC% 81- ebook / dp / B07H6XLXD7) -Create a simple CRUD with SpringBoot + JPA + Thymeleaf ② ~ Creating screens and functions ~

Recommended Posts

Create CRUD apps with Spring Boot 2 + Thymeleaf + MyBatis
Implement CRUD with Spring Boot + Thymeleaf + MySQL
Create your own Utility with Thymeleaf with Spring Boot
Create microservices with Spring Boot
Create an app with Spring Boot 2
Create an app with Spring Boot
Implement paging function with Spring Boot + Thymeleaf
Run WEB application with Spring Boot + Thymeleaf
Create a website with Spring Boot + Gradle (jdk1.8.x)
Until data acquisition with Spring Boot + MyBatis + PostgreSQL
Create a simple search app with Spring Boot
How to use MyBatis2 (iBatis) with Spring Boot 1.4 (Spring 4)
Create Spring Boot environment with Windows + VS Code
Create a web api server with spring boot
Download with Spring Boot
Create a Spring Boot development environment with docker
Create Spring Cloud Config Server with security with Spring Boot 2.0
Create Restapi with Spring Boot ((1) Until Run of App)
Create a simple demo site with Spring Security with Spring Boot 2.1
Generate barcode with Spring Boot
Hello World with Spring Boot
Implement GraphQL with Spring Boot
Get started with Spring boot
Hello World with Spring Boot!
Run LIFF with Spring Boot
SNS login with Spring Boot
[Java] Thymeleaf Basic (Spring Boot)
File upload with Spring Boot
Spring Boot starting with copy
Spring Boot starting with Docker
Hello World with Spring Boot
Set cookies with Spring Boot
Use Spring JDBC with Spring Boot
Add module with Spring Boot
Getting Started with Spring Boot
Send email with spring boot
Handle Java 8 date and time API with Thymeleaf with Spring Boot
Create a simple CRUD with SpringBoot + JPA + Thymeleaf ③ ~ Add Validation ~
Until INSERT and SELECT to Postgres with Spring boot and thymeleaf
Connect to database with spring boot + spring jpa and CRUD operation
Use thymeleaf3 with parent without specifying spring-boot-starter-parent in Spring Boot
Use Basic Authentication with Spring Boot
gRPC on Spring Boot with grpc-spring-boot-starter
Hot deploy with Spring Boot development
Database linkage with doma2 (Spring boot)
Spring Boot programming with VS Code
Until "Hello World" with Spring Boot
Inquiry application creation with Spring Boot
Get validation results with Spring Boot
Google Cloud Platform with Spring Boot 2.0.0
Check date correlation with Spring Boot
I tried GraphQL with Spring Boot
[Java] LINE integration with Spring Boot
Beginning with Spring Boot 0. Use Spring CLI
I tried Flyway with Spring Boot
Authentication / authorization with Spring Security & Thymeleaf
Thymeleaf usage notes in Spring Boot
Message cooperation started with Spring Boot
Spring Boot gradle build with Docker
Create a simple CRUD with SpringBoot + JPA + Thymeleaf ④ ~ Customize error messages ~
How to create your own Controller corresponding to / error with Spring Boot