使用 MySQL 存取資料

本指南將引導您完成建立連接到 MySQL 資料庫的 Spring 應用程式的流程(相對於大多數其他指南和許多範例應用程式使用的記憶體內嵌式資料庫)。它使用 Spring Data JPA 來存取資料庫,但這只是眾多可能的選擇之一(例如,您可以使用純 Spring JDBC)。

您將建置的內容

您將建立一個 MySQL 資料庫,建置一個 Spring 應用程式,並將其連接到新建立的資料庫。

MySQL 以 GPL 授權,因此您隨之發行的任何程式二進位檔也必須使用 GPL。請參閱 GNU 通用公共授權

您需要的東西

  • 約 15 分鐘

  • 您慣用的文字編輯器或 IDE

  • Java 17 或更高版本

如何完成本指南

與大多數 Spring 入門指南 一樣,您可以從頭開始並完成每個步驟,或者您可以直接跳到解決方案,透過檢視 此儲存庫 中的程式碼。

若要在本機環境中查看最終結果,您可以執行下列其中一項操作

設定 MySQL 資料庫

在您可以建置應用程式之前,您首先需要設定 MySQL 資料庫。本指南假設您使用 Spring Boot Docker Compose 支援。此方法的先決條件是您的開發機器具有 Docker 環境,例如 Docker Desktop。新增一個 spring-boot-docker-compose 相依性,其作用如下

  • 在您的工作目錄中搜尋 compose.yml 和其他常見的 compose 檔案名稱

  • 使用探索到的 compose.yml 呼叫 docker compose up

  • 為每個支援的容器建立服務連線 bean

  • 在應用程式關閉時呼叫 docker compose stop

若要使用 Docker Compose 支援,您只需要遵循本指南即可。根據您提取的相依性,Spring Boot 會找到正確的 compose.yml 檔案,並在您執行應用程式時啟動您的 Docker 容器。

從 Spring Initializr 開始

您可以使用此 預先初始化的專案,然後按一下「Generate」下載 ZIP 檔案。此專案已設定為符合本教學課程中的範例。

若要手動初始化專案

  1. 導覽至 https://start.spring.io。此服務會提取應用程式所需的所有相依性,並為您完成大部分設定。

  2. 選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇 Java。

  3. 按一下Dependencies,然後選取 Spring WebSpring Data JPAMySQL DriverDocker Compose SupportTestcontainers

  4. 按一下Generate

  5. 下載產生的 ZIP 檔案,這是一個已使用您的選擇設定的 Web 應用程式的封存檔。

如果您的 IDE 具有 Spring Initializr 整合,您可以從 IDE 完成此流程。

建立 @Entity 模型

您需要建立實體模型,如下列清單所示(在 src/main/java/com/example/accessingdatamysql/User.java 中)

package com.example.accessingdatamysql;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity // This tells Hibernate to make a table out of this class
public class User {
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Integer id;

  private String name;

  private String email;

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }
}

Hibernate 會自動將實體轉換為表格。

建立 Repository

您需要建立保存使用者記錄的 repository,如下列清單所示(在 src/main/java/com/example/accessingdatamysql/UserRepository.java 中)

package com.example.accessingdatamysql;

import org.springframework.data.repository.CrudRepository;

import com.example.accessingdatamysql.User;

// This will be AUTO IMPLEMENTED by Spring into a Bean called userRepository
// CRUD refers Create, Read, Update, Delete

public interface UserRepository extends CrudRepository<User, Integer> {

}

Spring 會在具有相同名稱的 bean 中自動實作此 repository 介面(大小寫會變更 — 它稱為 userRepository)。

建立 Controller

您需要建立一個控制器來處理應用程式的 HTTP 請求,如下列清單所示(在 src/main/java/com/example/accessingdatamysql/MainController.java 中)

package com.example.accessingdatamysql;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller // This means that this class is a Controller
@RequestMapping(path="/demo") // This means URL's start with /demo (after Application path)
public class MainController {
  @Autowired // This means to get the bean called userRepository
         // Which is auto-generated by Spring, we will use it to handle the data
  private UserRepository userRepository;

  @PostMapping(path="/add") // Map ONLY POST Requests
  public @ResponseBody String addNewUser (@RequestParam String name
      , @RequestParam String email) {
    // @ResponseBody means the returned String is the response, not a view name
    // @RequestParam means it is a parameter from the GET or POST request

    User n = new User();
    n.setName(name);
    n.setEmail(email);
    userRepository.save(n);
    return "Saved";
  }

  @GetMapping(path="/all")
  public @ResponseBody Iterable<User> getAllUsers() {
    // This returns a JSON or XML with the users
    return userRepository.findAll();
  }
}
先前的範例明確指定了兩個端點的 POSTGET。依預設,@RequestMapping 會對應所有 HTTP 作業。

建立 Application 類別

Spring Initializr 為應用程式建立了一個簡單的類別。下列清單顯示 Initializr 為此範例建立的類別(在 src/main/java/com/example/accessingdatamysql/AccessingDataMysqlApplication.java 中)

package com.example.accessingdatamysql;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccessingDataMysqlApplication {

  public static void main(String[] args) {
    SpringApplication.run(AccessingDataMysqlApplication.class, args);
  }

}

對於此範例,您不需要修改 AccessingDataMysqlApplication 類別。

Spring Initializr 將 @SpringBootApplication 註解新增至我們的主要類別。@SpringBootApplication 是一個便利的註解,會新增以下所有項目

  • @Configuration:將類別標記為應用程式內容的 bean 定義來源。

  • @EnableAutoConfiguration:Spring Boot 嘗試根據您新增的相依性自動設定您的 Spring 應用程式。

  • @ComponentScan:告訴 Spring 尋找其他元件、組態和服務。如果未定義特定套件,則遞迴掃描會從宣告註解的類別的套件開始。

執行應用程式

此時,您現在可以執行應用程式以查看您的程式碼運作。您可以透過 IDE 或從命令列執行 main 方法。請注意,如果您已從解決方案儲存庫複製專案,您的 IDE 可能會在錯誤的位置尋找 compose.yaml 檔案。您可以設定您的 IDE 在正確的位置尋找,或者您可以使用命令列執行應用程式。./gradlew bootRun./mvnw spring-boot:run 命令會啟動應用程式並自動尋找 compose.yaml 檔案。

測試應用程式

現在應用程式正在執行中,您可以使用 curl 或某些類似工具來測試它。您有兩個可以測試的 HTTP 端點

GET localhost:8080/demo/all:取得所有資料。POST localhost:8080/demo/add:將一個使用者新增至資料。

下列 curl 命令會新增使用者

$ curl https://127.0.0.1:8080/demo/add -d name=First -d [email protected]

回覆應如下所示

Saved

下列命令會顯示所有使用者

$ curl https://127.0.0.1:8080/demo/all

回覆應如下所示

[{"id":1,"name":"First","email":"[email protected]"}]

準備建置應用程式

若要封裝和執行應用程式,我們需要提供外部 MySQL 資料庫,而不是使用 Spring Boot Docker Compose 支援。對於此任務,我們可以重複使用提供的 compose.yaml 檔案,並進行一些修改:首先,將 compose.yaml 中的 ports 項目修改為 3306:3306。其次,新增 container_nameguide-mysql

完成這些步驟後,compose.yaml 檔案應為

services:
  mysql:
    container_name: 'guide-mysql'
    image: 'mysql:latest'
    environment:
      - 'MYSQL_DATABASE=mydatabase'
      - 'MYSQL_PASSWORD=secret'
      - 'MYSQL_ROOT_PASSWORD=verysecret'
      - 'MYSQL_USER=myuser'
    ports:
      - '3306:3306'

您現在可以執行 docker compose up 以啟動此 MySQL 容器。

第三,我們需要告訴我們的應用程式如何連接到資料庫。此步驟先前已透過 Spring Boot Docker Compose 支援自動處理。為此,請修改 application.properties 檔案,使其現在為

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mydatabase
spring.datasource.username=myuser
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.show-sql: true

建置應用程式

本節說明執行本指南的不同方式

無論您選擇如何執行應用程式,輸出都應該相同。

若要執行應用程式,您可以將應用程式封裝為可執行 jar。./gradlew clean build 命令會將應用程式編譯為可執行 jar。然後,您可以使用 java -jar build/libs/accessing-data-mysql-0.0.1-SNAPSHOT.jar 命令執行 jar。

或者,如果您有可用的 Docker 環境,您可以使用 buildpacks 直接從 Maven 或 Gradle 外掛程式建立 Docker 映像檔。透過 Cloud Native Buildpacks,您可以建立可在任何地方執行的 Docker 相容映像檔。Spring Boot 直接包含對 Maven 和 Gradle 的 buildpack 支援。這表示您可以輸入單一命令,並快速將合理的映像檔放入本機執行的 Docker daemon 中。若要使用 Cloud Native Buildpacks 建立 Docker 映像檔,請執行 ./gradlew bootBuildImage 命令。在啟用 Docker 環境的情況下,您可以使用 docker run --network container:guide-mysql docker.io/library/accessing-data-mysql:0.0.1-SNAPSHOT 命令執行應用程式。

--network 旗標會告訴 Docker 將我們的指南容器附加到現有網路,我們的外部容器正在使用該網路。您可以在 Docker 文件 中找到更多資訊。

原生映像檔支援

如果您在機器上安裝了 GraalVM 發行版,Spring Boot 也支援 編譯為原生映像檔。若要使用 Native Build Tools 使用 Gradle 建立原生映像檔,請先確定您的 Gradle 建置包含一個 plugins 區塊,其中包含 org.graalvm.buildtools.native

plugins {
	id 'org.graalvm.buildtools.native' version '0.9.28'
...

然後,您可以執行 ./gradlew nativeCompile 命令來產生原生映像檔。建置完成後,您將能夠透過執行 build/native/nativeCompile/accessing-data-mysql 命令,以接近瞬間的啟動時間執行程式碼。

您也可以 使用 Buildpacks 建立原生映像檔。您可以透過執行 ./gradlew bootBuildImage 命令來產生原生映像檔。建置完成後,您可以使用 docker run --network container:guide-mysql docker.io/library/accessing-data-mysql:0.0.1-SNAPSHOT 命令啟動您的應用程式。

在 Docker 中測試應用程式

如果您使用上述 Docker 指令執行應用程式,則從終端機或命令列執行的簡單 curl 命令將不再有效。這是因為我們正在 Docker 網路 中執行我們的容器,該網路無法從終端機或命令列存取。若要執行 curl 命令,我們可以啟動第三個容器來執行我們的 curl 命令,並將其附加到相同的網路。

首先,取得新容器的互動式 shell,該容器與 MySQL 資料庫和應用程式在相同的網路上執行

docker run --rm --network container:guide-mysql -it alpine

接下來,從容器內部的 shell 中,安裝 curl

apk add curl

最後,您可以執行 測試應用程式 中描述的 curl 命令。

進行一些安全性變更

當您處於生產環境時,您可能會暴露於 SQL 注入攻擊。駭客可能會注入 DROP TABLE 或任何其他破壞性 SQL 命令。因此,作為安全性實務,您應該在將應用程式暴露給使用者之前,對資料庫進行一些變更。

下列命令會撤銷與 Spring 應用程式相關聯之使用者的所有權限

mysql> revoke all on db_example.* from 'myuser'@'%';

現在 Spring 應用程式無法在資料庫中執行任何操作。

應用程式必須具有一些權限,因此請使用下列命令來授予應用程式所需的最少權限

mysql> grant select, insert, delete, update on db_example.* to 'myuser'@'%';

移除所有權限並授予一些權限,讓您的 Spring 應用程式擁有必要的權限,只能變更資料庫的資料,而不能變更結構 (schema)。

當您想要變更資料庫時

  1. 重新授予權限。

  2. spring.jpa.hibernate.ddl-auto 變更為 update

  3. 重新執行您的應用程式。

然後重複此處顯示的兩個命令,以再次讓您的應用程式安全地用於生產環境。更好的方法是使用專用的遷移工具,例如 Flyway 或 Liquibase。

摘要

恭喜!您剛剛開發了一個繫結到 MySQL 資料庫且已準備好用於生產環境的 Spring 應用程式!

另請參閱

下列指南也可能有所幫助

想要撰寫新的指南或貢獻現有的指南嗎?請查看我們的 貢獻指南

所有指南均以 ASLv2 授權發布程式碼,並以 姓名標示-禁止改作創用 CC 授權條款 發布寫作內容。

取得程式碼