使用 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 開始

你可以使用此預初始化的專案並點選“生成”下載 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 會自動將此 repository 介面實現為一個同名(大小寫有變化——它被稱為 userRepository)的 bean。

建立 Controller

你需要建立一個 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 或命令列執行主方法。請注意,如果你從解決方案倉庫克隆了專案,你的 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://:8080/demo/add -d name=First -d [email protected]

回覆應如下所示:

Saved

以下命令顯示所有使用者:

$ curl https://: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://: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 文件中找到更多資訊。

原生映象支援

Spring Boot 也支援編譯為原生映象,前提是你的機器上安裝了 GraalVM 發行版。要使用 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 命令,並將其附加到同一個網路中。

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

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 許可釋出,文字內容採用知識共享署名-禁止演繹許可 (Attribution, NoDerivatives creative commons license) 釋出。

獲取程式碼