上一篇文章,主要分析了怎么建立一个Restful web service,系列二主要创建一个H5静态页面使用ajax请求数据,功能主要有添加一本书,请求所有书并且按照Id降序排列,以及查看,删除一本书。

1.   示例结构以及用到的技术点

1.1 项目逻辑架构

1.2 项目的技术选型

  • Spring-Data-JPA
  • H5
  • Bootstrap
  • jQuery + ajax

2.  后端服务

2.1  pom.xml依赖的jar包和系列一中结构完全一样,省略不表。

2.2  book表结构,book类定义和系列一中定义完全一样,此处省略1000+英文字母。

2.3 在com.example.demo.store包中的BooRepository定义如下,

public interface BookRepository extends JpaRepository<Book,Integer> {

    @Query(value = "SELECT * FROM book order by Id desc", nativeQuery = true)
List<Book> findBooks();
}

2.4 因为项目简单的缘故,没有建立service包,在control中直接调用数据访问层接口。

@Controller
@RequestMapping(path = "/book")
public class BookController { @Autowired
private BookRepository bookRepository; @Value("${upload.path}")
private String filePath; @PostMapping(path = "/save")
public @ResponseBody String save(HttpServletRequest request, @RequestParam("poster") MultipartFile poster) { String tempPath = filePath;
String name = request.getParameter("name");
String category = request.getParameter("category");
Double price = Double.valueOf(request.getParameter("price"));
// Give today as a default date
Date publishDate = new Date();
String temp = request.getParameter("publishDate");
DateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
try {
publishDate = formatDate.parse(temp);
} catch (ParseException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
// Handle uploading picture
String fileName = poster.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 {
poster.transferTo(new File(tempPath));
bookRepository.save(book);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "Add new book successfully";
} @GetMapping(path = "/one/{id}")
public @ResponseBody Book findOne(@PathVariable("id") Integer id) throws NotFoundException { Optional<Book> book = this.bookRepository.findById(id);
if (book.isPresent()) {
return book.get();
} else {
throw new NotFoundException("Not found " + id);
}
} @GetMapping(path = "/all")
public @ResponseBody Iterable<Book> findAll() {
return bookRepository.findBooks();
} @DeleteMapping(path = "/del/{id}")
public @ResponseBody String deleteBook(@PathVariable("id") Integer id) {
bookRepository.deleteById(id);
return String.format("Delete book (%d) successfully!", id);
}
}

2.5 新建WebConfig文件,解决 springboot上传图片不能马上访问显示的问题

@Configuration
public class WebConfig implements WebMvcConfigurer { @Value("${upload.path}")
private String filePath; @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 映射图片保存地址
registry.addResourceHandler("/image/**").addResourceLocations("file:" + filePath);
}
}

// JSONWebConfig.java 详见系列一,完全一样,此处省略。

2.6 Application.properties内容如下:

值得一提的是连接MySQL数据库的url里面的时区用的是东八区的时间。

# DB connection configuration
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=Home2019 # JPA configuration
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true upload.path=D:/eclipse-workspace/ProductApp3/src/main/resources/static/image/

3.  前端H5

3.1 在public文件夹里面添加index.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></tbody>
</table>
</div> <button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#addModal">Add Book</button> <!-- Add Model -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add Book</h5>
</div>
<div class="modal-body">
<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" id="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" id="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" id="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" id="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" id="inputPoster" />
</div>
</div>
</div> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="saveBook">Save</button>
</div>
</div>
</div>
</div> <!-- View Model -->
<div class="modal fade" id="viewModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content" style="width:768px">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">View Book</h5>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="nameView" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="nameView" readonly>
</div>
</div>
<div class="form-group row">
<label for="categoryView" class="col-sm-2 col-form-label">Category</label>
<div class="col-sm-10">
<select class="form-control" id="categoryView" disabled>
<option>武侠</option>
<option>历史</option>
<option>军事</option>
<option>国学</option>
<option>投资</option>
<option>管理</option>
<option>传记</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="priceView" class="col-sm-2 col-form-label">Price</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="priceView" readonly>
</div>
</div>
<div class="form-group row">
<label for="dateView" class="col-sm-2 col-form-label">Publish Date</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="dateView" readonly>
</div>
</div>
<div class="form-group row">
<label for="posterView" class="col-sm-2 col-form-label">Poster</label>
<div class="col-sm-10">
<img src="..." alt="Poster" id="posterView">
</div>
</div>
</div> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div> <!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="js/jquery-3.4.1.js"></script>
<script src="js/book.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
</body>
</html>

3.2 在static/js文件夹里面添加book.js

 $(document).ready(function() {
$.ajax({
url: "/greeting"
}).then(function(data) {
$('#greeting-content').append(data.id + '---');
$('#greeting-content').append(data.content);
}); // Clean table
$('#books tr:not(:first)').empty(); $.getJSON('book/all').done(function (data) {
$.each(data, function (key, item) {
if (item != null) {
var tmp = JSON.stringify(item);
row = "<tr><td>" + item.id + "</td><td>" + item.name + "</td><td>" + item.category + "</td><td>" + item.price + "</td><td>" + '<button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#viewModal" onclick="viewBook('+item.id+')">View</button> <button type="button" class="btn btn-outline-secondary">Edit</button> <button type="button" class="btn btn-outline-secondary" onclick="delBook('+item.id+')">Delete</button>' + "</td><tr>";
$('#books tr:last').after(row);
}
});
});
}); $('#saveBook').on('click', function () {
// 处理图片上传
var fd = new FormData();
fd.append('name', $('#inputName').val());
fd.append('category', $('#categorySelect').val());
fd.append('price', $('#inputPrice').val());
fd.append('publishDate', $('#inputDate').val());
fd.append('poster', $('#inputPoster').get(0).files[0]); $.ajax({
url: 'book/save',
type: 'POST',
data: fd,
cache: false,
processData: false,
contentType: false,
success: function () {
alert('Add new book successfully!');
// 刷新父窗口
$('#addModel').modal('hide');
window.location.reload();
},
error: function (xhr,status,errorThrown) {
alert('Failed to add new book.Error :' + errorThrown);
}
});
}); function viewBook(id) {
$.ajax({
url: '/book/one/' + id,
// Whether this is a POST or GET request
type: "GET",
// The type of data we expect back
dataType : "json"
})
// Code to run if the request succeeds (is done);
// The response is passed to the function
.done(function(data) {
$('#viewModel').modal('show');
$('#nameView').val(data.name);
$('#categoryView').val(data.category);
$('#priceView').val(data.price);
$('#dateView').val(data.publish_date);
$('#posterView').attr('src',data.poster);
})
// Code to run if the request fails; the raw request and
// status codes are passed to the function
.fail(function( xhr, status, errorThrown ) {
alert( "Sorry, there was a problem!" );
console.log( "Error: " + errorThrown );
console.log( "Status: " + status );
console.dir( xhr );
})
// Code to run regardless of success or failure;
.always(function( xhr, status ) {
//alert( "The request is complete!" );
});
} function delBook(id) {
if(true == confirm("Are you sure to delete it?")) {
$.ajax({
url:'book/del/' + id,
type:'DELETE',
// The type of data we expect back
dataType : "json"
})
// Code to run if the request succeeds (is done);
// The response is passed to the function
.done(function(data) {
alert(data);
window.location.reload();
})
// Code to run if the request fails; the raw request and
// status codes are passed to the function
.fail(function( xhr, status, errorThrown ) {
alert( "Sorry, there was a problem!" );
console.log( "Error: " + errorThrown );
console.log( "Status: " + status );
console.dir( xhr );
})
// Code to run regardless of success or failure;
.always(function( xhr, status ) {
//alert( "The request is complete!" );
});
}
}

4.  运行程序

浏览器中输入http://localhost:8080/index.html,程序默认输出所有记录,按Id大小降序排列。

程序最终实现了添加,查看,删除功能。

Spring Boot进阶系列二的更多相关文章

  1. Spring Boot进阶系列一

    笔者最近在总结一个 Spring Boot实战系列,以方便将来查找和公司内部培训用途. 1.Springboot从哪里来 SpringBoot是由Pivotal团队在2013年开始研发.2014年4月 ...

  2. Spring Boot进阶系列三

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

  3. Spring Boot进阶系列四

    这边文章主要实战如何使用Mybatis以及整合Redis缓存,数据第一次读取从数据库,后续的访问则从缓存中读取数据. 1.0 Mybatis MyBatis 是支持定制化 SQL.存储过程以及高级映射 ...

  4. spring boot / cloud (十二) 异常统一处理进阶

    spring boot / cloud (十二) 异常统一处理进阶 前言 在spring boot / cloud (二) 规范响应格式以及统一异常处理这篇博客中已经提到了使用@ExceptionHa ...

  5. 【转】Spring Boot干货系列:(二)配置文件解析

    转自:Spring Boot干货系列:(二)配置文件解析 前言 上一篇介绍了Spring Boot的入门,知道了Spring Boot使用"习惯优于配置"(项目中存在大量的配置,此 ...

  6. Spring Boot干货系列:(二)配置文件解析

    Spring Boot干货系列:(二)配置文件解析 2017-02-28 嘟嘟MD 嘟爷java超神学堂   前言 上一篇介绍了Spring Boot的入门,知道了Spring Boot使用“习惯优于 ...

  7. Spring Boot干货系列:(十二)Spring Boot使用单元测试(转)

    前言这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求 Service层单元测试 Controller层单元测试 新断言assertThat使用 单元 ...

  8. Spring Boot入门系列(二十)快速打造Restful API 接口

    spring boot入门系列文章已经写到第二十篇,前面我们讲了spring boot的基础入门的内容,也介绍了spring boot 整合mybatis,整合redis.整合Thymeleaf 模板 ...

  9. spring boot高性能实现二维码扫码登录(上)——单服务器版

    前言 目前网页的主流登录方式是通过手机扫码二维码登录.我看了网上很多关于扫码登录博客后,发现基本思路大致是:打开网页,生成uuid,然后长连接请求后端并等待登录认证相应结果,而后端每个几百毫秒会循环查 ...

随机推荐

  1. tkmybatis VS mybatisplus

    本文是简单对比了以下官网上的内容 文章目录 TkMybatis Vs MybatisPlus 1.基础CRUD BaseMapper 2.代码生成器 3. 全局主键 Sequence主键 4. 热加载 ...

  2. 关于wordpress4.8中的Twenty Seventeen主题的主题选项增加章节的实现

    我这里的wordpress版本是4.8  默认的主题是 Twenty Seventeen 我想实现的事 主题选项的首页  多增加2个章节 默认是只有4个章节  我想在增加2个 到6个 看下实现后的效果 ...

  3. PIE SDK主成分变换

    1.算法功能简介   主成分变换(Principal Component Analysis,PCA)又称K-L(Karhunen-Loeve)变换或霍特林(Hotelling)变换,是基于变量之间的相 ...

  4. uni-app插件ColorUI步骤条

    1. uni-app插件ColorUI步骤条 1.1. 前言 uni-app就不介绍了,前面几篇已经有所介绍,不知道的可以翻看我前面几篇博客 ColorUI-uniApp是uni-app的一款ui组件 ...

  5. Qt中QWidget、QDialog和QMainWindow

    QWidget 类是所有用户界面对象的基类.只有一个"页面" QMainWindow 是一个"窗口".含有菜单栏.状态栏.工具栏.停靠窗口.中心窗口 QDial ...

  6. VUE--v-on修饰符

    1.v-on的修饰符 .stop:阻止事件冒泡 <div @click="getTitle"> 阿Q <button @click="getBut&qu ...

  7. NioCopy文件

    步骤: 1.创建输入输出流  fis fos 2.创建通道  fis.getchannel()  fos.getchannel(); 3.创建缓存区      ByteBuffer buffer = ...

  8. Memcache内存缓存框架

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10923221.html 一:Memcache是什么,为什么要用它 MemCache是一个高性能.“分布式”的 ...

  9. SpringCloud2.0 Hystrix Feign 基于Feign实现断路器

    原文:https://www.cnblogs.com/songlu/p/9968953.html 1.启动[服务中心]集群,工程名:springcloud-eureka-server 参考 Sprin ...

  10. 安装glibc

    wget http://ftp.gnu.org/gnu/glibc/glibc-2.23.tar.gztar -zxvf glibc-2.23.tar.gz cd glibc-2.23 mkdir b ...