建立一個SpringBoot專案,實現簡單的CRUD功能和分頁查詢

heyhy發表於2024-09-04

背景

本博文主要是建立了一個新的SpringBoot專案,實現基本的增刪改查,分頁查詢,帶條件的分頁查詢功能。是方便初學者學習後端專案的一個比較清晰明瞭的實踐程式碼,讀者可根據博文,從自己動手建立一個新的SpringBoot專案,到使用PostMan測試基本請求,完完全全實踐一遍,寫出自己的程式碼,或者實現自己想要的功能。因為在這個過程中會遇到許多的問題,從JDK的版本選擇到跑通SpringBoot專案,最後到成功發起一個請求。所以,最好是自己練習一下,最終做到遊刃有餘。

環境的安裝

本次SpringBoot專案主要用到,一是IDEA,二是MYSQL(服務端),三是DBeaver(一個資料庫的客戶端),四是PostMan(用於傳送HTTP請求)。
建議讀者先按照MYSQL包,再按照DBeaver, 調通資料庫以後,再開始編寫專案。

MYSQL和DBeaver的安裝和使用

mysql服務的安裝按照以下這個教程裝就好了。連結如下:https://blog.csdn.net/Sublime_16/article/details/124227417
需要注意的是,生成臨時密碼以後,千萬要先複製到一個文字檔案中,避免在啟動服務的時候,終端已經關閉,沒有臨時密碼,無法啟動服務;我就是因為臨時密碼沒有儲存下來,結果後面又重新去生成這個臨時密碼。浪費了一些時間。
另外就是,開啟終端的時候,使用管理員的身份執行
DBeaver是一個免費的、通用的資料庫管理工具,支援多種資料庫系統,包括 MySQL、PostgreSQL、SQLite、Oracle、SQL Server 等。它提供了圖形介面和強大的功能,使使用者能夠連線、管理和操作不同型別的資料庫。安裝和使用的連結如下:https://www.cnblogs.com/nigx128/p/Jf4QN.html
需要注意的是,如果出現了這樣的報錯,

可重新編輯連線,設定驅動屬性為true。

另外就是在一開始建立一個資料庫的時候,這裡不要寫具體的名稱。資料庫連線以後,再定義具體的資料庫名,mydatabase


我們本次的建表語句如下:

-- mydatabase.student_info definition

CREATE TABLE `student_info` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8mb3 NOT NULL DEFAULT '""',
  `address` varchar(100) CHARACTER SET utf8mb3 NOT NULL,
  `other` varchar(100) CHARACTER SET utf8mb3 NOT NULL,
  `age` int NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=macce;

建好表以後,往表中新增如下示例資料。新增好的表資料如下

建立一個新的SpringBoot專案

1、新建一個project;
這邊我們使用maven管理依賴,maven的依賴管理目錄可以單獨設定一個空目錄,自己比較好找到的目錄,有時候依賴下載失敗,需要從這個目錄中刪除一些依賴檔案;目錄的設定可在專案生成以後在IDEA中進行設定。然後jdk的版本有8 就選擇8, 沒有的話,這裡隨便選一下,後面編譯的時候,可以在IDEA中重新設定。

點選下一步,可以設定SpringBoot的版本。以及要在pom中引入哪些依賴包,方便我們後續寫程式碼的時候呼叫。我這邊簡單選了幾個,如果還有要使用的依賴,直接勾選就行,或者專案建好後手動在pom新增也可以。
,
點選create, 並使用new windows開啟,就得到一個新的SpringBoot專案。接下來就等待依賴下載成功,下載成功以後,maven的目錄是這樣的。本次專案的pom.xml檔案如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo3</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo3</name>
    <description>demo3</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

配置檔案 application.yml

配置檔案中的資料庫的使用者名稱和密碼都要和你實際進行資料庫連線時配置的一樣,url也是如此。
server.port: 8088 是tomcat的埠號。
com.mysql.cj.jdbc.Driver是一個驅動類,是MySQL Connector 驅動程式中的 JDBC 驅動類,用於在 Java 應用程式中建立與 MySQL 資料庫的連線。 一旦建立了連線,JDBC 驅動類允許 Java 應用程式執行 SQL 查詢、更新和其他資料庫操作。應用程式可以使用 JDBC API 與資料庫進行通訊,傳送和接收資料。JDBC 驅動類還支援事務管理。Java 應用程式可以使用 JDBC API 開啟、提交或回滾事務,確保資料庫操作的原子性、一致性、隔離性和永續性(ACID 屬性)。在發生錯誤或異常時,驅動類也會生成適當的異常,應用程式可以捕獲並處理這些異常。
載入驅動類是建立連線和執行資料庫操作的第一步!
url 指的是資料庫連線的 URL,它用於指定要連線的資料庫的位置和其他連線引數,localhost指的是本地的主機名,3306是mysql的預設埠號。mydatabase是要連線的資料庫的名稱。應用程式和資料庫通訊,首先會透過該url建立連線,不過root和password需要和資料庫連線配置中嚴格一致,否則會連不到資料庫。
有關hibernate 的這段配置意味著透過 JPA 和 Hibernate 配置了禁用自動 DDL(Data Definition Language)生成、列印執行的SQL語句到控制檯,以及指定了 Hibernate 使用的 MySQL 資料庫方言是 MySQL 5 的 InnoDB 儲存引擎方言。use_jdbc_metadata_defaults引數當設定為 false 時,Hibernate 將不使用 JDBC 後設資料的預設設定。
重點說明一下ddl-auto, 等於none是指Hibernate 不會嘗試自動建立、更新或刪除資料庫表結構。

server.port: 8088
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mydatabase
  jpa:
    hibernate:
      ddl-auto: none
    show-sql: true
    properties:
      hibernate:
        temp:
          use_jdbc_metadata_defaults: false
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect

業務程式碼

在Web後端應用程式中,Controller主要接收來自使用者的請求並根據請求呼叫適當的業務邏輯(Service層),然後將處理結果返回給使用者。
總結的說,Controller的作用是:1、接收請求;2、呼叫Service層的方法;3、引數解析,對請求頭中的引數進行解析;4、返回響應結果。將處理結果封裝成適當的響應(如HTML頁面、JSON資料等)返回給客戶端。我這邊為了方便起見,直接調repository中的方法了,後續補充一個Service層。
1、先寫一個Controller

package com.example.demo3.controller;

import com.example.demo3.repository.StudentEntity;
import com.example.demo3.repository.StudentRepository;
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.jpa.domain.Specification;
import org.springframework.web.bind.annotation.*;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.List;

@RestController // 注意,該註解表明是是rest風格的API, 返回值通常是資料物件,Spring 將資料轉換為JSON或者XML格式,返回給客戶端。
// controller返回的是檢視名稱或者ModelAndView物件。需要配置@ReponseBody使用,即在方法的前面加上這個註解。
@RequestMapping("/student")
public class StudentController {

    @Autowired
    StudentRepository repository;

    //查, 透過姓名查詢
    @RequestMapping(value = "/byname", method = {RequestMethod.GET})
    //@ResponseBody
    public List<StudentEntity> findByName(@RequestParam("name") String name) {
        return repository.findByName(name);
    }
    // 全部返回,線上我們一般都用分頁查詢,因為資料量比較大嘛。直接返回全部資料的介面用的很少或者幾乎不用。
    @GetMapping(value = "/all")
    public List<StudentEntity> findAlls() {
        List<StudentEntity> all = repository.findAll();
        return all;
    }

    // 增
    @PostMapping(value = "/add")
    private String addOne(@RequestBody StudentEntity student){
        repository.save(student);
        return "學生" + student.getName() + "新增成功";
    }

    // 改
    @PutMapping(value = "/update")
    private String updateOne(@RequestBody StudentEntity student){
        repository.save(student);
        return "學生" + student.getName() + "修改成功";
    }

    // 刪
    @DeleteMapping(value = "/delete")
    private String deleteOne(@RequestParam("id") Integer id) {
        repository.deleteById(id);
        return "學生" + id + "刪除成功";
    }

    // 簡單分頁查詢
    @GetMapping("/page")
    public Page<StudentEntity> findByPage(@RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "2") Integer size) {
        Pageable pageable = PageRequest.of(page, size);
        return repository.findAll(pageable);
    }

    // 帶條件複雜分頁查詢
    @GetMapping("/page2")
    public Page<StudentEntity> findByPage2(@RequestParam(value = "name") String name, @RequestParam(value = "age") Integer age, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "size", defaultValue = "2") Integer size) {
        Pageable pageable = PageRequest.of(page, size);
        Specification<StudentEntity> specification = prediacte(name, age);
        return repository.findAll(specification, pageable);
    }

    public static Specification<StudentEntity> prediacte(String name, Integer age) {
        return (Root<StudentEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) -> {
            // 這邊簡單用name和age查詢一下
            Predicate namePredicate = criteriaBuilder.equal(root.get("name"), name);
            Predicate agePredicate = criteriaBuilder.equal(root.get("age"), age);
            return criteriaBuilder.and(namePredicate, agePredicate);
        };
    }
}

2、Repository
這個是dao層,主要是執行一些和資料庫互動的功能。在和資料庫互動的時候,我們要建立一個實體類Entity, 這邊我們叫做StudentEntity實體物件,相應的,資料庫裡面也應該有和這個實體類一一對映的表。資料庫中的表名就可以寫在註解@Table裡。
@Entity註解的作用如下。首先, @Entity的含義是用於標識一個java類作為JPA(java persistence api)實體類, 表示該類將對映到資料庫中的表。也就是說,實體類和資料庫表的對映關係由該註解的實現,其中的對映關係具體體現在:實體類的屬性和資料庫表的欄位一一對應。
@id註解用於表示某個欄位作為表的主鍵,每個表必須要顯式定義一個主鍵。
@Column 註解是 JPA(Java Persistence API)中用於描述實體屬性與資料庫表列之間對映關係的註解。透過 @Column 註解,可以指定實體類屬性與資料庫表中列的對映細節,如列名、資料型別、長度、是否可為空等。即可以更精細地控制實體屬性與資料庫表列的對映關係。如程式碼所示,name中指定的列名和表中的對應起來,然後private 屬性就可以任意取一個名字了,不過建議還是都對應起來。
StudentEntity

package com.example.demo3.repository;

import lombok.Data;

import javax.persistence.*;

@Entity
@Table(name = "student_info")
@Data
public class StudentEntity {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "name")
    private String name;
    @Column(name = "age")
    private int age;
    @Column(name = "address")
    private String address;
    @Column(name = "other")
    private String other;
}

以下是StudentRepository類,這邊定義的幾個方法都是直接呼叫Spring JPA 封裝好的方法,可直接呼叫。如果遇到比較複雜的場景,我們也可以在這裡寫一個介面,再寫介面方法的具體實現。

package com.example.demo3.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import java.util.List;

public interface StudentRepository extends JpaRepository<StudentEntity, Integer>, JpaSpecificationExecutor<StudentEntity> {

    List<StudentEntity> findByName(String name);

    List<StudentEntity> findAll();

    void deleteById(Integer id);

    Page<StudentEntity> findAll(Pageable pageable);
}

測試結果

有兩種測試方法,可以在網頁端直接輸入url發起HTTP請求,由於我們的HTTP請求中要使用物件,所以選擇Postman會比較方便。
1、根據name查詢

2、增加一個student

3、修改id=xx的學生的資訊

4、刪除id=xx的學生資訊

5、分頁查詢

我們預設的是取第0頁的資料,每頁取size個資料。 故"pageSize": 2,"pageNumber": 0。 totalPages指的是總共有多少條資料, "totalPages": 3指的是一共要分多少頁。5條資料,每頁2個資料,那麼就是分3頁。"sort"是分頁時排序的屬性。
{
    "content": [
        {
            "id": 1,
            "name": "小紅",
            "age": 18,
            "address": "深圳市",
            "other": "女;本科畢業,程式設計師"
        },
        {
            "id": 2,
            "name": "小黑",
            "age": 29,
            "address": "廣州市",
            "other": "男;本科畢業,程式設計師"
        }
    ],
    "pageable": {
        "sort": {
            "empty": true,
            "sorted": false,
            "unsorted": true
        },
        "offset": 0,
        "pageSize": 2,
        "pageNumber": 0,
        "paged": true,
        "unpaged": false
    },
    "totalElements": 5,
    "last": false,
    "totalPages": 3,
    "number": 0,
    "size": 2,
    "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
    },
    "numberOfElements": 2,
    "first": true,
    "empty": false
}

6、帶條件的分頁查詢

{
    "content": [
        {
            "id": 2,
            "name": "小黑",
            "age": 29,
            "address": "廣州市",
            "other": "男;本科畢業,程式設計師"
        }
    ],
    "pageable": {
        "sort": {
            "empty": true,
            "sorted": false,
            "unsorted": true
        },
        "offset": 0,
        "pageNumber": 0,
        "pageSize": 2,
        "unpaged": false,
        "paged": true
    },
    "last": true,
    "totalPages": 1,
    "totalElements": 1,
    "number": 0,
    "size": 2,
    "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
    },
    "numberOfElements": 1,
    "first": true,
    "empty": false
}

總結

後面我會加一個Service層。今天就先寫這麼多吧。

相關文章