文章目录
前言:
用Springboot +JPA+thymeleaf+MySQL+BootStrap 部署了一个订单管理系统,实现了 添加设备、删除设备、更新设备、查找设备、分页、排序等功能,项目内分有不同的包,也就是层,下面我将对每层进行解释。
BootStrap官网
1.创建项目
1.1 项目结构
1.2 在application.properties配置数据库
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
#localhost:3306为mysql的host和port tesstdb就是连接的庶几乎库名称
#mysql用户名
spring.datasource.username= root
#登入mysql的密码
spring.datasource.password= 123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# for Spring Boot 2
# spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
# for Spring Boot 3
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQLDialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update
#?????hibernate-sql
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE
2.model模型层
package com.example.mvcorders.model;
import jakarta.persistence.*;
import lombok.Data;
@Data
@Entity
@Table(name = "orders")
public class Orders {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
//订单id
@Column(name = "order_id")
private Long orderID;
//时间
@Column(name = "time")
private String time;
//收件人
@Column(name = "consignee")
private String consignee;
//收件地址
@Column(name = "addr")
private String addr;
//订单状态
@Column(name = "status")
private String status;
}
3.repository层
此层只定义了一个接口,而且此接口继承了JpaRepository接口,JpaRepository接口内有很多方法,可供接下来的使用,因为JpaRepository接口里并不一定有我想要的功能的方法,这里我创建了一个模糊查询,可以通过status状态这个属性,查询到相关的所有信息
package com.example.mvcorders.repository;
import com.example.mvcorders.model.Orders;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface OrdersRepository extends JpaRepository<Orders,Long> {
@Query("select e from Orders e where e.status like %:status%")
List<Orders> findByStatus(@Param("status") String status);
}
4.service 业务层
业务层分别有两部分,一个是接口,一个是接口的实现类
4.1OrdersService接口
接口里负责构建方法,但没有实体,方法的实体由继承此接口的类重写,每个方法指定要实现什么功能
package com.example.mvcorders.service;
import com.example.mvcorders.model.Orders;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
public interface OrdersService {
//获取所有订单
List<Orders> getOrders();
//新增/更新一个订单
void saveOrders(Orders orders);
//获取指定ID的订单
Orders getOrdersById(long id);
//删除指定ID的订单
void deleteOrdersById(long id);
//通过指定状态查询信息
List<Orders> findByStatus(String status);
//分页
Page<Orders> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection);
}
4.2 OrdersServicelmpl类
此类需要继承OrdersService接口并实现父类方法
package com.example.mvcorders.service;
import com.example.mvcorders.model.Orders;
import com.example.mvcorders.repository.OrdersRepository;
import org.aspectj.weaver.ast.Or;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class OrdersServicelmpl implements OrdersService{
@Autowired
private OrdersRepository ordersRepository;
@Override //返回所有信息
public List<Orders> getOrders() {
return ordersRepository.findAll();
}
@Override //添加信息
public void saveOrders(Orders orders) {
this.ordersRepository.save(orders);
}
@Override //通过id查找信息
public Orders getOrdersById(long id) {
Optional<Orders> optional = ordersRepository.findById(id);
Orders orders = null;
if (optional.isPresent()){
orders = optional.get();
}else {
throw new RuntimeException("找不到订单ID:"+id);
}
return orders;
}
@Override //通过byID删除信息
public void deleteOrdersById(long id) {
this.ordersRepository.deleteById(id);
}
@Override //通过状态查询信息
public List<Orders> findByStatus(String status) {
return ordersRepository.findByStatus(status);
}
@Override //排序分页
public Page<Orders> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name())
? Sort.by(sortField).ascending()
: Sort.by(sortField).descending();
//根据页号/每页记录数/排序依据返回某指定页面数据。
Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
return this.ordersRepository.findAll(pageable);
}
}
5.Controller控制层
此层只定义了一个类,用于接收参数,调用Service层的方法的返回值进行封装,返回数据/返回页面,路由。
package com.example.mvcorders.controller;
import com.example.mvcorders.model.Orders;
import com.example.mvcorders.service.OrdersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
public class OrdersController {
@Autowired
private OrdersService ordersService;
@GetMapping("/")
public String viewHomePage(Model model) {
return findPaginated(1, "status", "asc", model);
}
//添加数据
@GetMapping("/showNewOrdersForm")
public String showNewOrdersForm(Model model) {
// 创建模型属性来绑定表单数据
Orders orders = new Orders();
//addAttribute方法用于向模型(Model)中添加属性(Attribute)供视图(View)使用。
//创建了一个名为orders,值为一个orders对象的属性
model.addAttribute("orders", orders);
//返回到net_orders 视图
return "new_orders";
}
// 将员工保存到数据库
@PostMapping("/saveOrders")
public String saveOrders(@ModelAttribute("orders") Orders orders) {
ordersService.saveOrders(orders);
return "redirect:/";
}
//更新数据
@GetMapping("/showFormForUpdate/{id}")
//@PathVariable 注解来获取在 URL 中动态传递的参数
public String showFormForUpdate(@PathVariable(value = "id") long id, Model model) {
// 从服务中获取员工
Orders orders = ordersService.getOrdersById(id);
// 将employee设置为模型属性以预填充表单
model.addAttribute("orders", orders);
return "update_orders";
}
//删除数据
@GetMapping("/deleteOrders/{id}")
public String deleteOrders(@PathVariable(value = "id") long id) {
// call delete employee method
this.ordersService.deleteOrdersById(id);
return "redirect:/";
}
//通过状态查询
@GetMapping("/find")
public String findStatus(@RequestParam("status") String status, Model model){
List<Orders> byStatus = ordersService.findByStatus(status);
model.addAttribute("byStatus", byStatus);
return "find_status";
}
//分页
@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable (value = "pageNo") int pageNo,
//@RequestParam 注解可以将这些查询参数绑定到处理器方法的参数上,从而方便获取和处理这些参数。
@RequestParam("sortField") String sortField,
@RequestParam("sortDir") String sortDir,
Model model) {
int pageSize = 8;
Page<Orders> page = ordersService.findPaginated(pageNo, pageSize, sortField, sortDir);
List<Orders> listOrders = page.getContent();
model.addAttribute("currentPage", pageNo);
model.addAttribute("totalPages", page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("sortField", sortField);
model.addAttribute("sortDir", sortDir);
model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");
model.addAttribute("listOrders", listOrders);
return "index";
}
}
6.HTML
6.1 Index.html部分
此部分用于是主体页面的代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Orders Management System</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
</head>
<body>
<div class="container my-2 border">
<h1 class="text-center">Orders Management System</h1>
<nav> <!--设置搜索栏-->
<form style="float: right;" th:action="@{/find}" method="get">
<input id="status" type="text" name="status" placeholder="Search...">
<button type="submit" class="btn-dark">Search</button>
</form>
</nav>
<!-- th:href 链接地址-->
<a th:href = "@{/showNewOrdersForm}" class="btn btn-dark btn-sm mb-3"> Add Orders </a>
<table border="1" class = "table table-condensed table-dark table-hover text-info table-bordered table-hover text-center">
<thead>
<tr>
<th>
<a th:href="@{'/page/' + ${currentPage} + '?sortField=orderID&sortDir=' + ${reverseSortDir}}">
orderID</a>
</th>
<th>
<a th:href="@{'/page/' + ${currentPage} + '?sortField=time&sortDir=' + ${reverseSortDir}}">
time</a>
</th>
<th>
<a th:href="@{'/page/' + ${currentPage} + '?sortField=consignee&sortDir=' + ${reverseSortDir}}">
consignee</a>
</th>
<th>
<a th:href="@{'/page/' + ${currentPage} + '?sortField=addr&sortDir=' + ${reverseSortDir}}">
addr</a>
</th>
<th>
<a th:href="@{'/page/' + ${currentPage} + '?sortField=status&sortDir=' + ${reverseSortDir}}">
status</a>
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- th:each 元素遍历-->
<tr th:each="orders : ${listOrders}">
<!--th:text 用于指定标签显示的内容-->
<td th:text="${orders.orderID}"></td>
<td th:text="${orders.time}"></td>
<td th:text="${orders.consignee}"></td>
<td th:text="${orders.addr}"></td>
<td th:text="${orders.status}"></td>
<!--定位到controller层下-->
<td>
<a th:href="@{/showFormForUpdate/{id}(id=${orders.id})}" class="btn btn-primary ">更新</a>
<a th:href="@{/deleteOrders/{id}(id=${orders.id})}" class="btn btn-danger">删除</a>
</td>
</tr>
</tbody>
</table>
<!-- th:if 条件判断,如果为真-->
<div th:if = "${totalPages > 1}">
<div class = "row col-sm-10">
<!--显示一共有几行数据-->
<div class = "col-sm-3">
Total Rows: [[${totalItems}]]
</div>
<!--显示页数-->
<div class = "col-sm-5">
<span th:each="i: ${#numbers.sequence(1, totalPages)}">
<a th:if="${currentPage != i}" th:href="@{'/page/' + ${i}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">[[${i}]]</a>
<span th:unless="${currentPage != i}">[[${i}]]</span>
</span>
</div>
<!--切换到下一页-->
<div class = "col-sm-1">
<a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${currentPage + 1}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Next</a>
<span th:unless="${currentPage < totalPages}">Next</span>
</div>
<!--切换到最后一页-->
<div class="col-sm-1">
<a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${totalPages}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Last</a>
<span th:unless="${currentPage < totalPages}">Last</span>
</div>
</div>
</div>
</div>
</body>
</html>
效果:
6.2 new_orders.html部分
此页面是添加数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Order Management System</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
</head>
<body>
<div class="container my-2 " >
<h1 class="text-center" style="color: #000000;">Order Management System</h1>
<hr>
<h2 style="text-align: center; color: #000000;">Save Orders</h2>
<!--添加数据的页面--> <!--th:action 表单提交的url地址 @{} URL表达式 -->
<form action="#" th:action="@{/saveOrders}" th:object="${orders}" method="POST">
<!--订单ID的输入--> <!--th:field 用于指明要跳转的链接 *{}选择表达式,一般用于展示对象属性-->
<input type="text" th:field="*{orderID}" placeholder="orderID" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<!--订单时间的输入-->
<input type="text" th:field="*{time}" placeholder="time" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<!--订单收件人的输入-->
<input type="text" th:field="*{consignee}" placeholder="consignee" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<!--订单收件地址的输入-->
<input type="text" th:field="*{addr}" placeholder="addr" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<!--订单状态的输入-->
<input type="text" th:field="*{status}" placeholder="status" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<div style="display: flex; justify-content: center; align-items: center;">
<button type="submit" class="btn btn-dark col-2">Save Orders</button>
</div>
</form>
<hr>
<div style="display: flex; color: #000000; justify-content: center; align-items: center;">
<a th:href="@{/}" style="text-align: center;">Back to Orders List</a>
</div>
</div>
</body>
</html>
效果:
6.3 update_orders.html部分
更新数据的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Order Management System</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
</head>
<body>
<div class="container my-2">
<h1 class="text-center" style="color: #000000;">Order Management System</h1>
<hr>
<h2 style="text-align: center; color: #000000;">Update Order</h2>
<form action="#" th:action="@{/saveOrders}" th:object="${orders}" method="POST">
<!-- Add hidden form field to handle update -->
<input type="hidden" th:field="*{id}"/>
<input type="text" th:field="*{orderID}" placeholder="orderID" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<input type="text" th:field="*{time}" placeholder="orderID" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<input type="text" th:field="*{consignee}" placeholder="consignee" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<input type="text" th:field="*{addr}" placeholder="addr" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<input type="text" th:field="*{status}" placeholder="status" class="form-control mx-auto mb-4 col-4" style="border: 1px solid #000000">
<div style="display: flex; justify-content: center; align-items: center;">
<button type="submit" class="btn btn-info col-2"> Update Orders</button>
</div>
</form>
<hr>
<div style="display: flex; color: #000000; justify-content: center; align-items: center;">
<a th:href="@{/}" style="text-align: center;">Back to Orders List</a>
</div>
</div>
</body>
</html>
效果: