上一篇文章,主要分析了怎么建立一个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. jmeter 如何获取一小时之前的时间戳

    正确答案: ${__intSum(${__time(/1000,)},-3600,)} 如果还要显示毫秒 ${__longSum(${__time},-3600000,)}

  2. 《 .NET并发编程实战》实战习题集 - 4 - 如何重用一次性资源

    如何重用以下一次性资源代码呢? string text; using (var stream = new StreamReader(path)) { text = stream.ReadToEnd() ...

  3. C#工具类OracleHelper,基于Oracle.ManagedDataAccess.Client封装

    基于Oracle.ManagedDataAccess.Client封装的Oracle工具类OracleHelper,代码如下: using System; using System.Data; usi ...

  4. oracle 自增序列实现 可作为主键

    好记性不如烂笔头 记录如何建立oracle 自增序列 表结构 create table TAB_USERINPUT ( RID VARCHAR2(20) not null, VARID VARCHAR ...

  5. php操作cassandra

    php操作  https://www.cnblogs.com/fakis/archive/2010/04/16/1976653.html 这篇文章还没试过.暂时留着有时间再实验

  6. zsh禁用自动更新

    编辑.oh-my-zsh/oh-my-zsh.sh文件 set DISABLE_AUTO_UPDATE = false # Check for updates on initial load... i ...

  7. windows下使用ssh(利用paramiko库)

    环境:python3.7.3 win7 or win10 1.首先下载paramiko库 命令:pip install paramiko 2.代码: import paramiko 创建一个 ssh ...

  8. springboot搭建dubbo+zookeeper简单案例

    背景:只是自己使用单机版zookeeper搭建dubbo的一个学习案例,记录成功的过程 1.搭建zookeeper坏境 使用docker来构建环境 1.1 拉取镜像:docker pull zooke ...

  9. 使用jave1.0.2将amr文件转成其他格式报错解决方案

    背景:最近需要将微信公众号里面用户发的语音文件转成其他格式的语音文件 介绍:在刚开始使用jave1.0.2 没有几行代码就可以实现,但是发现在转换的过程会报错,但是最后文件也转成功了,此时是在wind ...

  10. Linux文本编辑器Vim使用

    1. 插入 o 在光标下插入新行 a 在光标后插入 i         在光标前插入 O 在光标上一行插入新行 A 在光标行尾插入 I 在光标行首插入 2.光标定位 gg 到第一行行首 G 到最后一行 ...