(springboot + thymeleaf)增删改查——运动器材管理系统

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

使用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>  &nbsp; &nbsp;
					</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高级程序设计的内容之一,这也是我第一次做这个项目,我知道做出来的这个项目呈现的内容不是很好,我自己本身也有很多的不足之处,未来会更加努力。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值