• Spring Boot进阶系列三


    Thymeleaf是官方推荐的显示引擎,这篇文章主要介绍怎么让spring boot整合Thymeleaf.  它是一个适用于Web和独立环境的现代服务器端Java模板引擎。

    Thymeleaf的主要目标是给开发工作流程带来优雅的自然模板 - 可以在浏览器中正确显示的HTML,也可以用作静态原型,从而在开发团队中实现更强大的协作。通过Spring Framework模块,与喜欢的工具的集成,Thymeleaf是HTML5 JVM Web开发的理想选择。

     

    1.自然模板官方示例

    用Thymeleaf编写的HTML模板看起来和HTML一样工作

    <table>
      <thead>
        <tr>
          <th th:text="#{msgs.headers.name}">Name</th>
          <th th:text="#{msgs.headers.price}">Price</th>
        </tr>
      </thead>
      <tbody>
        <tr th:each="prod: ${allProducts}">
          <td th:text="${prod.name}">Oranges</td>
          <td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td>
        </tr>
      </tbody>
    </table>

     

    2.项目结构

    2.1 主要功能还是添加一本书,查看一本书的明细,以及返回所有的书籍。这次项目中用到两个数据表。

    CREATE TABLE `book` (
      `Id` int(11) NOT NULL AUTO_INCREMENT,
      `Name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
      `Category` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
      `Price` decimal(18,2) NOT NULL,
      `Publish_Date` date NOT NULL,
      `Poster` varchar(128) NOT NULL,
      PRIMARY KEY (`Id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=87 DEFAULT CHARSET=utf8
    
    CREATE TABLE `author` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
      `phone` varchar(16) COLLATE utf8_unicode_ci NOT NULL,
      `email` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
      `book_id` int(11) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

     

    2.2 和系列二中的示例几乎一样,多了一个templates文件夹用来存放thymeleaf文件。

    需要在application.properties文件中添加以下内容:

     

    spring.thymeleaf.cache=true
    spring.thymeleaf.prefix=classpath:/templates/
    spring.thymeleaf.suffix=.html
    spring.thymeleaf.mode=HTML5
    spring.thymeleaf.encoding=UTF-8
    spring.thymeleaf.servlet.content-type=text/html

     

    2.3 项目用到的技术如下,

    •   Spring-Data-JPA
    •   H5
    •   Bootstrap
    •   Thymeleaf
    •   数据库依旧是MySQL

    2.4 项目逻辑架构图

     

    2.5 完整的pom.xml如下,

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- jdbc -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <!-- springboot,jpa 整合包 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <!-- mysql 驱动包 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <!--根据MySQL server 版本实际情况变动 -->
                <version>8.0.17</version><!--$NO-MVN-MAN-VER$ -->
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>

    3.后端服务层

    3.1 com.example.demo.model包中新建Author,Book,BookViewModel类,

    @Entity
    public class Author implements Serializable {
        private static final long serialVersionUID = -3523362375538074534L;
        @Id
        private Integer id;
        private String name;
        private String phone;
        private String email;
        private Integer bookId;
    } //省略getter & setter方法
    
    @Entity
    public class Book implements Serializable {
        private static final long serialVersionUID = -3123479062966697145L;
        @Id
        private Integer id;
        private String name;
        private String category;
        private Double price;
        private Date publish_date;
        private String poster;
    } //省略getter & setter方法
    
    public class BookViewModel {
        private Book book;
        private Author author;
    } //省略getter & setter方法

    3.2com.example.demo.store包中,新建AuthorRepository,BookRepository接口

    public interface BookRepository extends JpaRepository<Book,Integer> {
        @Query(value = "SELECT * FROM book order by Id desc", nativeQuery = true)
        List<Book> findBooks();
    }
    
    public interface AuthorRepository extends JpaRepository<Author, Integer> {
        Author findByBookId(Integer bookId);
    }

    3.3com.example.demo.controller包中创建BookController类,

    @Controller
    @RequestMapping(path = "/book")
    public class BookController {
    
        @Autowired
        private BookRepository bookRepository;
        
        @Autowired
        private AuthorRepository authorRepository;
        
        @Value("${upload.path}")
        private String filePath;
        
        @GetMapping(path="/add")
        public ModelAndView add() {
            ModelAndView modelAndView = new ModelAndView("add");
            return modelAndView;
        }
    
        @PostMapping(path = "/save")
        public String save(HttpServletRequest request, @RequestParam("inputPoster") MultipartFile inputPoster) {
    
            String tempPath = filePath;
            String name = request.getParameter("inputName");
            String category = request.getParameter("categorySelect");
            Double price = Double.valueOf(request.getParameter("inputPrice"));
            // Give today as a default date
            Date publishDate = new Date();
            String temp = request.getParameter("inputDate");
            DateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
            // Test only
            try {
                publishDate = formatDate.parse(temp);
            } catch (ParseException e2) {
                // TODO Auto-generated catch block
                e2.printStackTrace();
            }
            // Handle uploading picture
            String fileName = inputPoster.getOriginalFilename();
            String suffixName = fileName.substring(fileName.lastIndexOf('.'));
            fileName = UUID.randomUUID() + suffixName;
            String posterPath = "image/" + fileName;
            Book book = new Book();
            book.setId(0);
            book.setName(name);
            book.setCategory(category);
            book.setPrice(price);
            book.setPublish_date(publishDate);
            book.setPoster(posterPath);
            
            tempPath += fileName;
            try {
                inputPoster.transferTo(new File(tempPath));
                bookRepository.save(book);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return "redirect:/book/all";
        }
    
        @GetMapping(path = "/all")
        public ModelAndView findAll() {
            ModelAndView modelAndView = new ModelAndView("home");
            Iterable<Book> books = bookRepository.findBooks();
            modelAndView.addObject("bookList", books);
            return modelAndView;
        }
        
        
        @GetMapping(path="view/{id}")
        public ModelAndView view(@PathVariable Integer id) {
            ModelAndView mv = new ModelAndView("view");
            Optional<Book> optional = bookRepository.findById(id);
            Book book = optional.orElseGet(Book::new);
            Author author = authorRepository.findByBookId(id);
            if(author == null) {
                author = new Author();
            }
            BookViewModel bookViewModel = new BookViewModel();
            bookViewModel.setAuthor(author);
            bookViewModel.setBook(book);
            mv.addObject("bookView",bookViewModel);
            return mv;
            
        }
    }

    3.4com.example.demo.config 包中的WebConfig类和示例二中完全一样,此处省略不提。

    4.前端显示层

    在templates文件夹里面添加三个文件,分别是home.html, add.html, view.html.

    4.1 home.html

    <!doctype html>
    <html lang="en">
    <head>
    <!-- Required meta tags -->
    <meta charset="utf-8"></meta>
    <meta name="viewport"
        content="width=device-width, initial-scale=1, shrink-to-fit=no"></meta>
    
    <!-- Bootstrap CSS -->
    <link rel="stylesheet"
        href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
        crossorigin="anonymous"></link>
    
    <title>Index Page</title>
    </head>
    <body>
        <div class="container">
            <h1>Springboot进阶系列三</h1>
    
            <!-- Content here -->
            <div class="table-responsive">
                <table class="table table-striped table-sm" id="books">
                    <thead>
                        <tr>
                            <th>ID</th>
                            <th>Name</th>
                            <th>Category</th>
                            <th>Price</th>
                            <th>Operation</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr th:each="book : ${bookList}">
                            <td th:text="${book.id}"></td>
                            <td th:text="${book.name}"></td>
                            <td th:text="${book.category}"></td>
                            <td th:text="${book.price}"></td>
                            <td><a class="btn btn-outline-primary"
                                th:href="@{/book/view/{id}(id=${book.id})}" role="button">View</a>
                                <a class="btn btn-outline-primary"
                                th:href="@{/book/edit/{id}(id=${book.id})}" role="button">Edit</a>
                                <a class="btn btn-outline-primary"
                                th:href="@{/book/delete/{id}(id=${book.id})}" role="button">Delete</a></td>
                        </tr>
                    </tbody>
                </table>
            </div>
    
            <a href="/book/add" class="btn btn-outline-primary" role="button"
                aria-pressed="true">Add Book</a>
        </div>
    </body>
    </html>

    4.2 add.html

    <!doctype html>
    <html lang="en">
    <head>
    <!-- Required meta tags -->
    <meta charset="utf-8"></meta>
    <meta name="viewport"
        content="width=device-width, initial-scale=1, shrink-to-fit=no"></meta>
    
    <!-- Bootstrap CSS -->
    <link rel="stylesheet"
        href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
        crossorigin="anonymous"></link>
    
    <title>Add Book</title>
    </head>
    <body>
        <div class="container">
            <h1>Springboot进阶系列三</h1>
            <!-- Content here -->
            <form action="/book/save" method="POST" enctype="multipart/form-data">
                <div class="form-group row">
                    <label for="inputName" class="col-sm-2 col-form-label">Name</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" name="inputName"
                            placeholder="Name">
                    </div>
                </div>
                <div class="form-group row">
                    <label for="categorySelect" class="col-sm-2 col-form-label">Category</label>
                    <div class="col-sm-10">
                        <select class="form-control" name="categorySelect">
                            <option>武侠</option>
                            <option>历史</option>
                            <option>军事</option>
                            <option>国学</option>
                            <option>投资</option>
                            <option>管理</option>
                            <option>传记</option>
                        </select>
                    </div>
                </div>
                <div class="form-group row">
                    <label for="inputPrice" class="col-sm-2 col-form-label">Price</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" name="inputPrice"
                            placeholder="Price">
                    </div>
                </div>
                <div class="form-group row">
                    <label for="inputDate" class="col-sm-2 col-form-label">Publish
                        Date</label>
                    <div class="col-sm-10">
                        <input type="date" class="form-control" name="inputDate" />
                    </div>
                </div>
                <div class="form-group row">
                    <label for="inputPoster" class="col-sm-2 col-form-label">Poster</label>
                    <div class="col-sm-10">
                        <input type="file" class="form-control" name="inputPoster" />
                    </div>
                </div>
                <div class="form-group row">
                    <div class="col-sm-10">
                        <button type="submit" class="btn btn-outline-primary">Save</button>
                    </div>
                </div>
            </form>
        </div>
    </body>
    </html>

    4.3 view.html

    <!doctype html>
    <html lang="en">
    <head>
    <!-- Required meta tags -->
    <meta charset="utf-8"></meta>
    <meta name="viewport"
        content="width=device-width, initial-scale=1, shrink-to-fit=no"></meta>
    
    <!-- Bootstrap CSS -->
    <link rel="stylesheet"
        href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
        crossorigin="anonymous"></link>
    
    <title>View Book</title>
    </head>
    <body>
        <div class="container">
            <h1>Springboot进阶系列三</h1>
            <!-- Content here -->
            
                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">Name</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" th:value="${bookView.Book.name}">
                    </div>
                </div>
                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">Category</label>
                    <div class="col-sm-10">
                        <select class="form-control" th:value="${bookView.Book.category}">
                            <option>武侠</option>
                            <option>历史</option>
                            <option>军事</option>
                            <option>国学</option>
                            <option>投资</option>
                            <option>管理</option>
                            <option>传记</option>
                        </select>
                    </div>
                </div>
                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">Price</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" th:value="${bookView.Book.price}">
                    </div>
                </div>
                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">Publish
                        Date</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" th:value="${#dates.format(bookView.Book.publish_date,'yyyy-MM-dd')}" />
                    </div>
                </div>
                <div class="form-group row">
                    <label for="inputPoster" class="col-sm-2 col-form-label">Poster</label>
                    <div class="col-sm-10">
                        <img th:src="${'../../' + bookView.Book.poster}" alt="Poster"></img>
                    </div>
                </div>
                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">Author</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" th:value="${bookView.Author.name}">
                    </div>
                </div>
                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">Phone</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" th:value="${bookView.Author.phone}">
                    </div>
                </div>
                <div class="form-group row">
                    <label class="col-sm-2 col-form-label">Email</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" th:value="${bookView.Author.email}">
                    </div>
                </div>
                <div class="form-group row">
                    <div class="col-sm-10">
                        <a href="/book/all" class="btn btn-outline-dark" role="button" aria-pressed="true">Back</a>
                    </div>
                </div>
    
        </div>
    </body>
    </html>

    5.运行程序

    浏览器中输入http://localhost:8080/book/all,显示如下,

     

    点击 Add Book按钮,跳转到add.html页面,

     

  • 相关阅读:
    2020 CCPC Wannafly Winter Camp Day2 E阔力梯的树(树上启发式合并)
    牛客练习赛73D 离别(线段树)
    从零开始部署图书管理系统
    linux下安装nginx(编译安装)及反向代理及负载均衡
    linux下MariaDB安装
    linux下virtualenvwrapper安装
    linux下安装虚拟环境
    linux下安装django2.2
    linux下安装nginx(yum源安装)
    linux系统优化命令--day03
  • 原文地址:https://www.cnblogs.com/sankt/p/11525111.html
Copyright © 2020-2023  润新知