Spring Boot进阶系列二
上一篇文章,主要分析了怎么建立一个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进阶系列二的更多相关文章
- Spring Boot进阶系列一
笔者最近在总结一个 Spring Boot实战系列,以方便将来查找和公司内部培训用途. 1.Springboot从哪里来 SpringBoot是由Pivotal团队在2013年开始研发.2014年4月 ...
- Spring Boot进阶系列三
Thymeleaf是官方推荐的显示引擎,这篇文章主要介绍怎么让spring boot整合Thymeleaf. 它是一个适用于Web和独立环境的现代服务器端Java模板引擎. Thymeleaf的主要 ...
- Spring Boot进阶系列四
这边文章主要实战如何使用Mybatis以及整合Redis缓存,数据第一次读取从数据库,后续的访问则从缓存中读取数据. 1.0 Mybatis MyBatis 是支持定制化 SQL.存储过程以及高级映射 ...
- spring boot / cloud (十二) 异常统一处理进阶
spring boot / cloud (十二) 异常统一处理进阶 前言 在spring boot / cloud (二) 规范响应格式以及统一异常处理这篇博客中已经提到了使用@ExceptionHa ...
- 【转】Spring Boot干货系列:(二)配置文件解析
转自:Spring Boot干货系列:(二)配置文件解析 前言 上一篇介绍了Spring Boot的入门,知道了Spring Boot使用"习惯优于配置"(项目中存在大量的配置,此 ...
- Spring Boot干货系列:(二)配置文件解析
Spring Boot干货系列:(二)配置文件解析 2017-02-28 嘟嘟MD 嘟爷java超神学堂 前言 上一篇介绍了Spring Boot的入门,知道了Spring Boot使用“习惯优于 ...
- Spring Boot干货系列:(十二)Spring Boot使用单元测试(转)
前言这次来介绍下Spring Boot中对单元测试的整合使用,本篇会通过以下4点来介绍,基本满足日常需求 Service层单元测试 Controller层单元测试 新断言assertThat使用 单元 ...
- Spring Boot入门系列(二十)快速打造Restful API 接口
spring boot入门系列文章已经写到第二十篇,前面我们讲了spring boot的基础入门的内容,也介绍了spring boot 整合mybatis,整合redis.整合Thymeleaf 模板 ...
- spring boot高性能实现二维码扫码登录(上)——单服务器版
前言 目前网页的主流登录方式是通过手机扫码二维码登录.我看了网上很多关于扫码登录博客后,发现基本思路大致是:打开网页,生成uuid,然后长连接请求后端并等待登录认证相应结果,而后端每个几百毫秒会循环查 ...
随机推荐
- elasticsearch6设置默认分片数和副本数
elasticsearch6设置索引的默认分片数和副本数已经不是在elasticsearch.yml文件中了,而是使用了一个索引模板的东西 curl -XPUT 'http://10.27.12.16 ...
- JS操作摄像头
<script src="javascript/jquery-1.9.1.min.js"></script> <fieldset> <le ...
- vue基础之data
使用 调用data onLoad(option) { _self = this; _self.$data.xxxx = "te"; } 绑定节点 元素~~~~ <input ...
- ionic4 页面跳转传值和新页面取值
页面跳转 : <ion-row *ngFor="let item of aboutData.stockData" [routerLink]="[ '/stock-d ...
- 基于DockerSwarm 部署InfluxDB并使用JAVA操作
Docker中部署InfluxDB 1.运行容器 $ docker run --rm \ -e INFLUXDB_DB=db0 -e INFLUXDB_ADMIN_ENABLED=true \ -e ...
- PIE SDK 基于Dot net bar实现比例尺控件
最近在搭建主界面的过程中,为了界面美观大方,使用了Dot net bar.但是,在Dot net bar的状态栏中放置PIE SDK自带的比例尺控件,运行主界面程序后,比例尺控件始终不显示比例尺信息, ...
- 消息队列mq总结
一.消息队列概述消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构.目前使用较多的消息队列有ActiveMQ,RabbitM ...
- ELK——Elasticsearch 搭建集群经验
本文内容 背景 ES集群中第一个master节点 ES slave节点 迁移到:http://www.bdata-cap.com/newsinfo/1712679.html 本文总结 Elastics ...
- 2019-09-16 PHP CURL CURLOPT参数说明(curl_setopt)
CURLOPT_RETURNTRANSFER 选项: curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); 如果成功只将结果返回,不自动输出任何内容. 如果失败返回F ...
- Flutter 徐徐图之(一)—— 从搭建开发环境到 Hello World
一.环境变量 由于众所周知的原因,在国内访问 Flutter 有时可能会受到限制,所以在开发之前,需要先配置环境变量 MacOS: 编辑 ~/.bash_profile 文件 vim ~/.bash_ ...