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.2 在com.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.3 在com.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.4 在com.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页面,
原文地址:https://www.cnblogs.com/sankt/p/11525111.html
- JFinal极速开发框架使用笔记(四) _JFinalDemoGenerator实体类生成及映射自动化
- Python语言做数据探索教程
- Java常用工具类之时间转换(注释乱码,全)
- Java常用工具类之RegexpUtils,正则表达式工具类
- 短信接口发送验证码倒计时以及提交验证
- Java常用工具类之IO流工具类
- JFinal极速开发框架使用笔记
- JavaWeb项目之电话本,两个版本,以及总结反思
- 工作中问题记录
- Java导出数据生成Excel表格
- Layui常见问题
- layui动态设置下拉框数据,根据后台数据设置选中
- BCryptPasswordEncoder加密及判断密码是否相同
- 两个HTML,CSS布局实例
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- ArrayList源码分析(基于jdk1.8)(二):subList陷阱补充
- Windows10中安装Docker
- Windows下Docker安装ClickHouse
- ArrayList源码分析(基于jdk1.8)(三):Arrays.asList方法带来的问题
- 对基本类型包装类常量池的补充
- 与IntegerCache有关的一个比较坑的面试题
- C# Foreach循环本质与枚举器
- Java中的时间和日期(一):有关java时间的哪些坑
- Java中的时间和日期(二):java时间存储的基本原理
- 常用SQL语句
- Java中的时间和日期(三):java8中新的时间API介绍
- Java中的时间和日期(四):与java8时间API有关的一些总结和补充
- Head First设计模式——策略模式
- 可重用性的6个级别
- 您可能不需要使用Vue 3的Vuex