程序员社区

Spring Boot 整合 Bootstrap、Spring Data JPA 基础实战

  本文以单表数据的增删改查业务为背景,介绍 Spring Boot 整合视图层Bootstrap框架、Thymeleaf 模板以及 Spring Data JPA等框架的实际应用。

1、技术选型:

  • Spring Boot
  • Thymeleaf
  • Bootstrap
  • Spring Data JPA
  • MySQL

2、需求描述:实现图书信息单表数据的增删改查任务。
3、项目演示效果如下图所示:
在这里插入图片描述


1. 项目初始化

第一步:在 IDEA 中新建空白的 SpringBoot 工程,勾选如下几项:
在这里插入图片描述
注:这些勾选的依赖都会在生成空白项目时,自动导入到 pom.xml 文件中。
第二步:pom.xml 文件中手动添加整合 Bootstrap 的相关依赖

<!-- bootstrap、jquery前端框架等 -->
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>bootstrap</artifactId>
    <version>3.3.7</version>
</dependency>
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.1.1</version>
</dependency>
<dependency>
    <groupId>net.sourceforge.nekohtml</groupId>
    <artifactId>nekohtml</artifactId>
</dependency>

第三步:在 MySQL 数据库中创建一张书籍数据表(book),并录入若干条测试数据。
在这里插入图片描述

DROP TABLE IF EXISTS `book`;
CREATE TABLE `book`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `title` varchar(55) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `price` decimal(10, 2) NULL DEFAULT NULL,
  `publishDate` date NULL DEFAULT NULL,
  `publishName` varchar(55) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `authorInfo` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

创建数据表对应的实体类 Student

package com.trainingl.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;

import javax.persistence.*;
import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "book")   //实体类映射数据表
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    @Column
    private Double price;
    @Column
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date publishDate;
    @Column
    private String publishName;
    @Column
    private String authorInfo;
}

2. 业务代码

2.1 Spring Boot 配置文件

在全局配置文件 resources/application.yml 中配置数据源信息、视图解析器以及端口号等相关配置等

server:
  port: 8080
spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    mode: HTML5
    encoding: UTF-8
  datasource:
    url: jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

说明:最后一行表示 Spring Data JPA 的命名策略,这里取消了字段驼峰式命名时,字段转化为下划线连接的方式。

2.2.1 数据持化层

在路径 com > trainingl > repository 下创建接口 BookRepository

Spring Data JPA 不是对 JPA 规范的具体实现,本身是一个抽象层,底层也是通过 Hibernate 实现的。开发者使用 Spring Data JPA 持久化框架时,并不需要自己编写 SQL 语句去实现,而是将数据表和实体类映射起来后,直接继承 JpaRepository 即可,该类内置了与增删改查业务相关的一些方法可供调用。

package com.trainingl.repository;

import com.trainingl.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
//第一个泛型表示实体类的类型,第二个泛型表示主键类型
public interface BookRepository  extends JpaRepository<Book, Long> {
    public Book getById(Long id);
}

说明:第一个泛型表示实体类的类型,第二个泛型表示主键类型。

2.2.2 业务层

创建业务层接口 BookService

package com.trainingl.service;

import com.trainingl.entity.Book;

import java.util.List;

public interface BookService {
    public List<Book> findAll();
    public Book findById(Long id);
    public void save(Book book);
    public void update(Book book);
    public void deleteById(Long id);
}

业务层实现 BookServiceImpl

package com.trainingl.service.Impl;

import com.trainingl.entity.Book;
import com.trainingl.repository.BookRepository;
import com.trainingl.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookRepository bookRepository;

    @Override
    public List<Book> findAll() {
        return bookRepository.findAll();
    }

    @Override
    public Book findById(Long id) {
        return bookRepository.getById(id);
    }

    @Override
    public void save(Book book) {
        bookRepository.save(book);
    }

    @Override
    public void update(Book book) {
        bookRepository.save(book);
    }

    @Override
    public void deleteById(Long id) {
        bookRepository.deleteById(id);
    }
}

2.2.3 控制层

创建 BookController 控制器,主要负责接收客户端浏览器的请求与响应。这里接受的请求都是 POST 表单和 <a>...<a/> 标签触发的 GET 请求,至于 Ajax 异步请求在后面的实例学习中会进一步介绍。

package com.trainingl.controller;

import com.trainingl.entity.Book;
import com.trainingl.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/book")
public class BookController {

    @Autowired
    private BookService bookService;

    @GetMapping("/list")
    public ModelAndView booklist(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("index");
        modelAndView.addObject("books",bookService.findAll());
        return modelAndView;
    }

    //根据id查找数据库的记录
    @GetMapping("/findById/{id}")
    public ModelAndView findById(@PathVariable Long id){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("editor");
        Book book = bookService.findById(id);
        modelAndView.addObject("book",book);
        return modelAndView;
    }

    @PostMapping("/save")
    public String save(Book book){
        bookService.save(book);
        return "redirect:/book/list";
    }

    //根据id删除数据库的记录
    @GetMapping("/delete/{id}")
    public String deleteById(@PathVariable Long id){
        bookService.deleteById(id);
        return "redirect:/book/list";
    }

    //根据id修改数据记录
    @PostMapping("/update")
    public String updateById(Book book){
        bookService.update(book);
        return "redirect:/book/list";
    }
}

2.2.4 视图层

Bootstrap 是最受欢迎的 HTML、CSS 和 JS 框架,⽤于开发响应式布局、移动设备优先的WEB 项⽬。简洁、直观、强悍的前端开发框架,html、css、javascript ⼯具集,让 web 开发更速、简单。这里我使用 Bootstrap + Thymeleaf 模板完成视图层网页的搭建与数据渲染操作。
1、数据列表首页 resources > templates > index.html
在这里插入图片描述

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<html xmlns:th="http://www.thymeleaf.org"></html>
<head>
    <meta charset="UTF-8">
    <script src="/webjars/jquery/3.1.1/jquery.min.js"></script>
    <script src="/webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/webjars/bootstrap/3.3.7/css/bootstrap.min.css" />
    <title>数据列表</title>
</head>
<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" datatoggle="collapse" data-target="#bs-example-navbar-collapse-1" ariaexpanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="#">图书管理系统</a>
            </div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="active"><a href="#">图书信息<span class="sr-only">
    (current)</span></a></li>
                    <li><a href="#">新书推荐</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown"
                           role="button" aria-haspopup="true" aria-expanded="false">更多操作 <span
                                class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">Action</a></li>
                            <li><a href="#">Another action</a></li>
                            <li><a href="#">Something else here</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">Separated link</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">One more separated link</a></li>
                        </ul>
                    </li>
                </ul>
                <form class="navbar-form navbar-left">
                    <div class="form-group">
                        <input type="text" class="form-control" placeholder="搜素更多图书...">
                    </div>
                    <button type="submit" class="btn btn-default">搜索</button>
                </form>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#">Training.L</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown"
                           role="button" aria-haspopup="true" aria-expanded="false">用户管理 <span
                                class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">修改密码</a></li>
                            <li><a href="#">添加地址</a></li>
                            <li><a href="#">订单记录</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="#">退出登录</a></li>
                        </ul>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- 布局 3:7 -->
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-3">
                <div class="list-group">
                    <a href="#" class="list-group-item active">
                        首页
                    </a>
                    <a href="#" class="list-group-item">图书列表</a>
                    <a href="#" class="list-group-item">出版社列表</a>
                    <a href="#" class="list-group-item">作者信息列表</a>
                    <a href="#" class="list-group-item">关于我们</a>
                </div>
            </div>

            <div class="col-md-9">
                <button class="btn btn-success" data-toggle="modal" id="btn">添加新书</button>
                <br><br>
                <div class="panel panel-default">
                    <div class="panel-heading">书籍信息列表</div>
                    <table class="table table-hover table-striped">
                        <thead>
                        <tr>
                            <th>编号</th>
                            <th>书籍名称</th>
                            <th>价格</th>
                            <th>出版日期</th>
                            <th>图书出版社</th>
                            <th>作者信息</th>
                            <th>操作</th>
                        </tr>
                        </thead>
                        <tbody>
                        <tr th:each="book:${books}">
                            <td th:text="${book.id}"></td>
                            <td th:text="${book.title}"></td>
                            <td th:text="${book.price}"></td>
                            <td th:text="${#dates.format(book.publishDate,'yyyy-MM-dd')}"></td>
                            <td th:text="${book.publishName}"></td>
                            <td th:text="${book.authorInfo}"></td>
                            <td>
                                <a th:href="@{/book/findById/{cid}(cid=${book.id})}" class="btn btn-primary">编辑</a>
                                <a th:href="@{/book/delete/{id}(id=${book.id})}" class="btn btn-danger">删除</a>
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <div class="modal fade" tabindex="-1" role="dialog" id="myModal">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <h4 class="modal-title">添加新书籍</h4>
                </div>
                <div class="modal-body">
                    <form th:action="@{/book/save}" method="post" class="form-horizontal" role="form">
                        <div class="form-group">
                            <label for="title" class="col-md-2 control-label">图书标题
                            </label>
                            <div class="col-md-8">
                                <input type="text" id="title" name="title" class="form-control" placeholder="请输入图书标题...">
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="price" class="col-md-2 control-label">图书价格
                            </label>
                            <div class="col-md-8">
                                <input type="text" id="price" name="price" class="form-control" placeholder="请输入图书价格...">
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="date" class="col-md-2 control-label">出版日期
                            </label>
                            <div class="col-md-8">
                                <input type="date" id="date" name="publishDate" class="form-control" placeholder="请输入出版日期...">
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="publish" class="col-md-2 control-label">出版社名
                            </label>
                            <div class="col-md-8">
                                <input type="text" id="publish" name="publishName" class="form-control" placeholder="请输入出版社...">
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="author" class="col-md-2 control-label">图书作者
                            </label>
                            <div class="col-md-8">
                                <input type="text" id="author" name="authorInfo" class="form-control" placeholder="请输入图书作者...">
                            </div>
                        </div>
                        <button type="reset" class="btn btn-primary">重置</button>
                        <button type="submit" class="btn btn-success">提交</button>
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>

                    <button type="button" class="btn btn-success" id="submit_btn">保存提交</button>
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript">
        //绑定按钮的点击事件
        $("#btn").click(function(){
            // 手动打开模态框
            $("#myModal").modal('show');
        });
        //手动关闭模态框
        $("#submit_btn").click(function(){
            $('#myModal').modal('hide');
        });
    </script>
</body>
</html>

2、更新信息页面 resources > templates > editor.html
在这里插入图片描述

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<html xmlns:th="http://www.thymeleaf.org"></html>
<head>
    <meta charset="UTF-8">
    <script src="/webjars/jquery/3.1.1/jquery.min.js"></script>
    <script src="/webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="/webjars/bootstrap/3.3.7/css/bootstrap.min.css" />
    <title>修改图书信息</title>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-inverse">
    <div class="container-fluid">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" datatoggle="collapse" data-target="#bs-example-navbar-collapse-1" ariaexpanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">图书管理系统</a>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">图书信息<span class="sr-only">
    (current)</span></a></li>
                <li><a href="#">新书推荐</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown"
                       role="button" aria-haspopup="true" aria-expanded="false">更多操作 <span
                            class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">Separated link</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">One more separated link</a></li>
                    </ul>
                </li>
            </ul>
            <form class="navbar-form navbar-left">
                <div class="form-group">
                    <input type="text" class="form-control" placeholder="搜素更多图书...">
                </div>
                <button type="submit" class="btn btn-default">搜索</button>
            </form>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="#">Training.L</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown"
                       role="button" aria-haspopup="true" aria-expanded="false">用户管理 <span
                            class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">修改密码</a></li>
                        <li><a href="#">添加地址</a></li>
                        <li><a href="#">订单记录</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">退出登录</a></li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>

<!-- 布局 3:7 -->
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
            <div class="list-group">
                <a href="#" class="list-group-item active">
                    首页
                </a>
                <a href="#" class="list-group-item">图书列表</a>
                <a href="#" class="list-group-item">出版社列表</a>
                <a href="#" class="list-group-item">作者信息列表</a>
                <a href="#" class="list-group-item">关于我们</a>
            </div>
        </div>
        <div class="col-md-9">
            <h1 class="text-center text-muted">更新图书信息</h1>
            <hr>
            <form th:action="@{/book/update}" method="post" class="form-horizontal" role="form">
                <div class="form-group">
                    <label for="ID" class="col-sm-2 control-label">图书编号:</label>
                    <div class="col-sm-10">
                        <input type="text" name="id" id="ID" class="form-control"
                              th:value="${book.id}" readonly>
                    </div>
                </div>
                <div class="form-group">
                    <label for="title" class="col-sm-2 control-label">书籍名称:</label>
                    <div class="col-sm-10">
                        <input type="text" name="title" id="title" class="form-control"
                               th:value="${book.title}">
                    </div>
                </div>
                <div class="form-group">
                    <label for="price" class="col-sm-2 control-label">书籍价格:</label>
                    <div class="col-sm-10">
                        <input type="text" name="price" id="price" class="form-control"
                               th:value="${book.price}">
                    </div>
                </div>
                <div class="form-group">
                    <label for="date" class="col-sm-2 control-label">出版日期:</label>
                    <div class="col-sm-10">
                        <input type="date" name="publishDate" id="date" class="form-control"
                               th:value="${#dates.format(book.publishDate,'yyyy-MM-dd')}">
                    </div>
                </div>
                <div class="form-group">
                    <label for="publish" class="col-sm-2 control-label">图书出版社:</label>
                    <div class="col-sm-10">
                        <input type="text" name="publishName" id="publish" class="form-control"
                               th:value="${book.publishName}">
                    </div>
                </div>
                <div class="form-group">
                    <label for="author" class="col-sm-2 control-label">作者列表:</label>
                    <div class="col-sm-10">
                        <input type="text" name="authorInfo" id="author" class="form-control"
                               th:value="${book.authorInfo}">
                    </div>
                </div>
                <hr>
                <input type="submit" value="提交修改" class="btn btn-primary btn-block">
            </form>
        </div>
    </div>
</div>
</body>
</html>

总结:本文使用 Spring Boot 可以非常方便地集成视图层、持久化层的相关框架,本实例基于常见的单表 CRUD 业务,采用了 Bootstrap 前端框架搭建页面结构,并通过 Thymeleaf 模板渲染数据,后端的数据库交互采用了 Spring 体系中的 Spring Data JPA,完成了对单表增删改查的场景实现。尽管还存在很多细节部分需要完善,但初步具备了基本的项目架构。

赞(0) 打赏
未经允许不得转载:IDEA激活码 » Spring Boot 整合 Bootstrap、Spring Data JPA 基础实战

一个分享Java & Python知识的社区