提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
使用Java和MySQL编写的运动器材管理系统,前端使用了Bootstrap框架,运动器材管理系统功能有:添加器材功能,更新器材功能,删除器材功能,查看器材功能,还有分页功能(超过5行自动分页)。
一、运行环境
IDEA + Spring Boot 3 + Spring Framework6 + Maven + Java17 + Spring Data JPA + Thymaleaf
二、项目效果
三、项目构建
1.选择依赖
2.项目结构
没关系,只要人和代码有一个能跑就行。
3.Springboot配置文件
使用jdbc连接到数据库
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf-8
spring.datasource.username= root
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
四、前端页面代码实现
1.前台页面
首页面,就是上边项目展示的首页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta charset="ISO-8859-1">
<title>运动器材管理系统</title>
<!-- <link rel="stylesheet" href="http://127.0.0.1:8080/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">-->
<link rel="stylesheet" href="http://127.0.0.1:8080/bootstrap.min.css" >
</head>
<body>
<div class="container my-2">
<h1>运动器材管理系统</h1>
<a th:href = "@{/showNewGymnasiumForm}" class="btn btn-primary btn-sm mb-3"> 添加器材 </a>
<table border="3" class = "table table-striped table-responsive-md">
<!-- <nav class="navbar navbar-light bg-light">-->
<form id="query-form" action="/query" method="get">
<h6 class="text-secondary"></h6>
<input type="text" id="name-input" name="inpname" placeholder="器材名称">
<button type="button" class="btn btn-info" onclick="queryGymnasium()">查询</button>
</form>
<script>
function queryGymnasium() {
var name = document.getElementById("name-input").value;
var url = "/query/" + name;
window.location.href = url;
}
</script>
<!-- </nav>-->
<table border="3" class = "table table-striped table-responsive-md">
<thead>
<tr>
<th>
<a th:href="@{'/page/' + ${currentPage} + '?sortField=name&sortDir=' + ${reverseSortDir}}">
器材名称</a>
</th>
<th>
<a th:href="@{'/page/' + ${currentPage} + '?sortField=deposit_region&sortDir=' + ${reverseSortDir}}">
存放区域</a>
</th>
<th>
<a th:href="@{'/page/' + ${currentPage} + '?sortField=category&sortDir=' + ${reverseSortDir}}">
类别</a>
</th>
<th> 选项 </th>
</tr>
</thead>
<tbody>
<tr th:each="gymnasium : ${listGymnasiums}">
<td th:text="${gymnasium.name}"></td>
<td th:text="${gymnasium.deposit_region}"></td>
<td th:text="${gymnasium.category}"></td>
<td> <a th:href="@{/showFormForUpdate/{id}(id=${gymnasium.id})}" class="btn btn-primary">更新</a>
<a th:href="@{/deleteGymnasium/{id}(id=${gymnasium.id})}" class="btn btn-danger">删除</a>
</td>
</tr>
</tbody>
</table>
<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}}">器材总数:</a>
<span th:unless="${currentPage != i}"> </span>
</span>
</div>
<div class = "col-sm-1">
<a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${currentPage + 1}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">下一页</a>
<span th:unless="${currentPage < totalPages}"> </span>
</div>
<div class="col-sm-1">
<a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${totalPages}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">上一页</a>
<span th:unless="${currentPage < totalPages}"> </span>
</div>
</div>
</div>
</div>
</body>
</html>
2.新增功能界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>器材录入</title>
<!-- <link rel="stylesheet" href="http://127.0.0.1:8080/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">-->
<link rel="stylesheet" href="http://127.0.0.1:8080/bootstrap.min.css" >
</head>
<body>
<div class="container">
<h1>运动器材管理系统</h1>
<hr>
<h2>器材录入</h2>
<form action="#" th:action="@{/saveGymnasium}" th:object="${gymnasium}" method="POST">
<input type="text" th:field="*{name}" placeholder="器材名称" class="form-control mb-4 col-4">
<input type="text" th:field="*{deposit_region}" placeholder="器材存放区域" class="form-control mb-4 col-4">
<input type="text" th:field="*{category}" placeholder="器材类别" class="form-control mb-4 col-4">
<button type="submit" class="btn btn-info col-2"> 保存数据</button>
</form>
<hr>
<a th:href="@{/}"> 返回清单列表</a>
</div>
</body>
</html>
3.更新功能界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- <link rel="stylesheet" href="http://127.0.0.1:8080/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">-->
<link rel="stylesheet" href="http://127.0.0.1:8080/bootstrap.min.css" >
</head>
<body>
<div class="container">
<h1>运动器材管理系统</h1>
<hr>
<h2>器材更新</h2>
<form action="#" th:action="@{/saveGymnasium}" th:object="${gymnasium}" method="POST">
<!-- Add hidden form field to handle update -->
<input type="hidden" th:field="*{id}" />
<input type="text" th:field="*{name}" placeholder="器材名称" class="form-control mb-4 col-4">
<input type="text" th:field="*{deposit_region}" placeholder="器材存放区域" class="form-control mb-4 col-4">
<input type="text" th:field="*{category}" placeholder="器材类别" class="form-control mb-4 col-4">
<button type="submit" class="btn btn-info col-2"> 器材更新</button>
</form>
<hr>
<a th:href="@{/}"> 返回清单列表</a>
</div>
</body>
</html>
五、后端业务实现
1.实体类Gymnasium
作用:存放实体类的模型,其中包含器材名称、存放区域以及类别
package com.example.gymnasium.model;
import jakarta.persistence.*;
import lombok.Data;
@Data
@Entity
@Table(name = "gymnasiums")
public class Gymnasium {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "name") //器材名称
private String name;
@Column(name = "deposit_region") //存放区域
private String deposit_region;
@Column(name = "category") //类别
private String category;
}
2.操作层Repository
作用:主要是针对数据进行操作,包括很多对不同的数据库(比如MySQL、redis、mongodb)中的数据进行整合。
package com.example.gymnasium.repository;
import com.example.gymnasium.model.Gymnasium;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
public interface GymnasiumRepository extends JpaRepository<Gymnasium, Long> {
List<Gymnasium> findAllByName(String name);
}
3.业务代码Service接口
作用:service层主要负责业务模块的逻辑应用设计,如一些数据的检验,可以通用处理,与数据层交互等。
package com.example.gymnasium.service;
import com.example.gymnasium.model.Gymnasium;
import org.springframework.data.domain.Page;
import java.util.List;
public interface GymnasiumService {
List <Gymnasium> getAllGymnasiums();
List<Gymnasium>getAllGymnasium();
List<Gymnasium>findAllByName(String name);
void saveGymnasium(Gymnasium gymnasium);
Gymnasium getGymnasiumById(long id);
void deleteGymnasiumById(long id);
Page<Gymnasium> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection);
}
4.业务实现类
package com.example.gymnasium.service;
import com.example.gymnasium.model.Gymnasium;
import com.example.gymnasium.repository.GymnasiumRepository;
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 GymnasiumServiceImpl implements GymnasiumService {
@Autowired
private GymnasiumRepository gymnasiumRepository;
@Override
public List <Gymnasium> getAllGymnasiums() {
return gymnasiumRepository.findAll();
}
@Override
public List<Gymnasium> getAllGymnasium() {
return null;
}
@Override
public List<Gymnasium>findAllByName(String name) {
List<Gymnasium> gymnasiumname = gymnasiumRepository.findAllByName(name);
return gymnasiumname;
}
@Override
public void saveGymnasium(Gymnasium gymnasium) {
this.gymnasiumRepository.save(gymnasium);
}
@Override
public Gymnasium getGymnasiumById(long id) {
Optional <Gymnasium> optional = gymnasiumRepository.findById(id);
Gymnasium gymnasium = null;
if (optional.isPresent()) {
gymnasium = optional.get();
}else {
throw new RuntimeException("找不到这套运动器材 :: "+ id);
}
return gymnasium;
}
@Override
public void deleteGymnasiumById(long id){
this.gymnasiumRepository.deleteById(id);
}
@Override
public Page<Gymnasium> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
//设置排序参数,升序ASC/降序DESC?
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.gymnasiumRepository.findAll(pageable);
}
}
5.控制层Web接口
作用:与前端交互。返回给前端标识。不写复杂代码,只判断逻辑,根据判断返回不同的结果,具体的代码实现细节,隐藏在service层。
含有分页功能(超过5行自动分行)
package com.example.gymnasium.controller;
import com.example.gymnasium.model.Gymnasium;
import com.example.gymnasium.service.GymnasiumService;
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 GymnasiumController {
@Autowired
private GymnasiumService gymnasiumService;
@GetMapping("/")
public String viewHomePage(Model model) {return findPaginated(1,"name","asc",model); }
@GetMapping("/bootstrap.min.css")
public String css(){
return "bootstrap.min.css";
}
@GetMapping("/showNewGymnasiumForm")
public String showNewGymnasium(Model model) {
// create model attribute to bind form data
Gymnasium gymnasium = new Gymnasium();
model.addAttribute("gymnasium", gymnasium);
return "new_gymnasium";
}
@PostMapping("/saveGymnasium")
public String saveGymnasium(@ModelAttribute("gymnasium") Gymnasium gymnasium) {
// save gymnasium to database
gymnasiumService.saveGymnasium(gymnasium);
return "redirect:/";
}
@GetMapping("/showFormForUpdate/{id}")
public String showFormForUpdate(@PathVariable(value = "id") long id, Model model) {
// get gymnasium from the service
Gymnasium gymnasium = gymnasiumService.getGymnasiumById(id);
// set gymnasium as a model attribute to pre-populate the form
model.addAttribute("gymnasium", gymnasium);
return "update_gymnasium";
}
@GetMapping("/deleteGymnasium/{id}")
public String deleteGymnasium(@PathVariable(value = "id") long id) {
// call delete gymnasium method
this.gymnasiumService.deleteGymnasiumById(id);
return "redirect:/";
}
@GetMapping("/query/{newGymnasium}")
public String query(@PathVariable(value = "newGymnasium")String newGymnasium,Model model){
List<Gymnasium> listGymnasium = gymnasiumService.findAllByName(newGymnasium);
model.addAttribute("listGymnasiums",listGymnasium);
return "index";
}
//获取分页数据
@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable (value = "pageNo") int pageNo,
@RequestParam("sortField") String sortField,
@RequestParam("sortDir") String sortDir,
Model model) {
int pageSize = 5;
Page<Gymnasium> page = gymnasiumService.findPaginated(pageNo, pageSize, sortField, sortDir);
List<Gymnasium> listGymnasiums = 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("listGymnasiums", listGymnasiums);
return "index";
}
}
六、总结
这是我学习Java高级程序设计的内容之一,这也是我第一次做这个项目,我知道做出来的这个项目呈现的内容不是很好,我自己本身也有很多的不足之处,未来会更加努力。