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

  1 <!doctype html>
  2 <html lang="en">
  3 <head>
  4 <!-- Required meta tags -->
  5 <meta charset="utf-8"></meta>
  6 <meta name="viewport"
  7     content="width=device-width, initial-scale=1, shrink-to-fit=no"></meta>
  8 
  9 <!-- Bootstrap CSS -->
 10 <link rel="stylesheet"
 11     href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
 12     integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
 13     crossorigin="anonymous"></link>
 14 
 15 <title>Index Page</title>
 16 </head>
 17 <body>
 18     <div class="container">
 19         <h1>Springboot进阶系列二</h1>
 20         
 21         <!-- Content here -->
 22         <div class="table-responsive">
 23             <table class="table table-striped table-sm" id="books">
 24                 <thead>
 25                     <tr>
 26                         <th>ID</th>
 27                         <th>Name</th>
 28                         <th>Category</th>
 29                         <th>Price</th>
 30                         <th>Operation</th>
 31                     </tr>
 32                 </thead>
 33                 <tbody></tbody>
 34             </table>
 35         </div>
 36         
 37         <button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#addModal">Add Book</button>
 38         
 39         <!-- Add Model -->
 40         <div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
 41             <div class="modal-dialog" role="document">
 42                 <div class="modal-content">
 43                     <div class="modal-header">
 44                         <h5 class="modal-title" id="exampleModalLabel">Add Book</h5>
 45                     </div>
 46                     <div class="modal-body">
 47                         <div class="form-group row">
 48                             <label for="inputName" class="col-sm-2 col-form-label">Name</label>
 49                             <div class="col-sm-10">
 50                                 <input type="text" class="form-control" id="inputName" placeholder="Name">
 51                             </div>
 52                         </div>
 53                         <div class="form-group row">
 54                             <label for="categorySelect" class="col-sm-2 col-form-label">Category</label>
 55                             <div class="col-sm-10">
 56                                 <select class="form-control" id="categorySelect">
 57                                     <option>武侠</option>
 58                                     <option>历史</option>
 59                                     <option>军事</option>
 60                                     <option>国学</option>
 61                                     <option>投资</option>
 62                                     <option>管理</option>
 63                                     <option>传记</option>
 64                                 </select>
 65                             </div>
 66                         </div>
 67                         <div class="form-group row">
 68                             <label for="inputPrice" class="col-sm-2 col-form-label">Price</label>
 69                             <div class="col-sm-10">
 70                                 <input type="text" class="form-control" id="inputPrice" placeholder="Price">
 71                             </div>
 72                         </div>
 73                         <div class="form-group row">
 74                             <label for="inputDate" class="col-sm-2 col-form-label">Publish Date</label>
 75                             <div class="col-sm-10">
 76                                 <input type="date" class="form-control" id="inputDate" />
 77                             </div>
 78                         </div>
 79                         <div class="form-group row">
 80                             <label for="inputPoster" class="col-sm-2 col-form-label">Poster</label>
 81                             <div class="col-sm-10">
 82                                 <input type="file" class="form-control" id="inputPoster" />
 83                             </div>
 84                         </div>
 85                     </div>
 86 
 87                     <div class="modal-footer">
 88                         <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
 89                         <button type="button" class="btn btn-primary" id="saveBook">Save</button>
 90                     </div>
 91                 </div>
 92             </div>
 93         </div>
 94         
 95         <!-- View Model -->
 96         <div class="modal fade" id="viewModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
 97             <div class="modal-dialog" role="document">
 98                 <div class="modal-content" style="width:768px">
 99                     <div class="modal-header">
100                         <h5 class="modal-title" id="exampleModalLabel">View Book</h5>
101                     </div>
102                     <div class="modal-body">
103                         <div class="form-group row">
104                             <label for="nameView" class="col-sm-2 col-form-label">Name</label>
105                             <div class="col-sm-10">
106                                 <input type="text" class="form-control" id="nameView" readonly>
107                             </div>
108                         </div>
109                         <div class="form-group row">
110                             <label for="categoryView" class="col-sm-2 col-form-label">Category</label>
111                             <div class="col-sm-10">
112                                 <select class="form-control" id="categoryView" disabled>
113                                     <option>武侠</option>
114                                     <option>历史</option>
115                                     <option>军事</option>
116                                     <option>国学</option>
117                                     <option>投资</option>
118                                     <option>管理</option>
119                                     <option>传记</option>
120                                 </select>
121                             </div>
122                         </div>
123                         <div class="form-group row">
124                             <label for="priceView" class="col-sm-2 col-form-label">Price</label>
125                             <div class="col-sm-10">
126                                 <input type="text" class="form-control" id="priceView" readonly>
127                             </div>
128                         </div>
129                         <div class="form-group row">
130                             <label for="dateView" class="col-sm-2 col-form-label">Publish Date</label>
131                             <div class="col-sm-10">
132                                 <input type="text" class="form-control" id="dateView" readonly>
133                             </div>
134                         </div>
135                         <div class="form-group row">
136                             <label for="posterView" class="col-sm-2 col-form-label">Poster</label>
137                             <div class="col-sm-10">
138                                 <img src="..." alt="Poster" id="posterView">
139                             </div>
140                         </div>
141                     </div>
142 
143                     <div class="modal-footer">
144                         <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
145                     </div>
146                 </div>
147             </div>
148         </div>
149     </div>
150 
151 
152     <!-- Optional JavaScript -->
153     <!-- jQuery first, then Popper.js, then Bootstrap JS -->
154     <script src="js/jquery-3.4.1.js"></script>
155     <script src="js/book.js"></script>
156     <script
157         src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
158         integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
159         crossorigin="anonymous"></script>
160     <script
161         src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
162         integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
163         crossorigin="anonymous"></script>
164 </body>
165 </html>

 

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

 

  1 $(document).ready(function() {
  2     $.ajax({
  3         url: "/greeting"
  4     }).then(function(data) {
  5        $('#greeting-content').append(data.id + '---');
  6        $('#greeting-content').append(data.content);
  7     });
  8     
  9     // Clean table
 10     $('#books tr:not(:first)').empty();
 11 
 12     $.getJSON('book/all').done(function (data) {
 13         $.each(data, function (key, item) {
 14             if (item != null) {
 15                 var tmp = JSON.stringify(item);
 16                 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>";
 17                 $('#books tr:last').after(row);
 18             }
 19         });
 20     });
 21 });
 22 
 23 
 24 $('#saveBook').on('click', function () {
 25     // 处理图片上传
 26     var fd = new FormData();
 27     fd.append('name', $('#inputName').val());
 28     fd.append('category', $('#categorySelect').val());
 29     fd.append('price', $('#inputPrice').val());
 30     fd.append('publishDate', $('#inputDate').val());
 31     fd.append('poster', $('#inputPoster').get(0).files[0]);
 32 
 33     $.ajax({
 34         url: 'book/save',
 35         type: 'POST',
 36         data: fd,
 37         cache: false,
 38         processData: false,
 39         contentType: false,
 40         success: function () {
 41             alert('Add new book successfully!');
 42             // 刷新父窗口
 43             $('#addModel').modal('hide');
 44             window.location.reload();
 45         },
 46         error: function (xhr,status,errorThrown) {
 47             alert('Failed to add new book.Error :' + errorThrown);
 48         }
 49     });
 50 });
 51 
 52 
 53 function viewBook(id) {
 54     $.ajax({
 55         url: '/book/one/' + id, 
 56         // Whether this is a POST or GET request
 57         type: "GET", 
 58         // The type of data we expect back
 59         dataType : "json"
 60     })
 61       // Code to run if the request succeeds (is done);
 62       // The response is passed to the function
 63       .done(function(data) {
 64          $('#viewModel').modal('show');
 65          $('#nameView').val(data.name);
 66          $('#categoryView').val(data.category);
 67          $('#priceView').val(data.price);
 68          $('#dateView').val(data.publish_date);
 69          $('#posterView').attr('src',data.poster);
 70       })
 71       // Code to run if the request fails; the raw request and
 72       // status codes are passed to the function
 73       .fail(function( xhr, status, errorThrown ) {
 74         alert( "Sorry, there was a problem!" );
 75         console.log( "Error: " + errorThrown );
 76         console.log( "Status: " + status );
 77         console.dir( xhr );
 78       })
 79       // Code to run regardless of success or failure;
 80       .always(function( xhr, status ) {
 81         //alert( "The request is complete!" );
 82       });
 83 }
 84 
 85 
 86 function delBook(id) {
 87     if(true == confirm("Are you sure to delete it?")) {
 88         $.ajax({
 89             url:'book/del/' + id,
 90             type:'DELETE',
 91             // The type of data we expect back
 92             dataType : "json"
 93         })
 94           // Code to run if the request succeeds (is done);
 95           // The response is passed to the function
 96           .done(function(data) {
 97               alert(data);
 98               window.location.reload();      
 99           })
100           // Code to run if the request fails; the raw request and
101           // status codes are passed to the function
102           .fail(function( xhr, status, errorThrown ) {
103             alert( "Sorry, there was a problem!" );
104             console.log( "Error: " + errorThrown );
105             console.log( "Status: " + status );
106             console.dir( xhr );
107           })
108           // Code to run regardless of success or failure;
109           .always(function( xhr, status ) {
110             //alert( "The request is complete!" );
111           });
112     }
113 }

 

4.  运行程序

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

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

 

 

 

posted @ 2019-09-08 23:11  沐璟  阅读(464)  评论(0编辑  收藏  举报