使用 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. 单击“依赖项”,然后选择“Spring Web”、“Spring Data JPA”、“MySQL 驱动程序”、“Docker Compose 支持”和“Testcontainers”。

  4. 单击“生成”。

  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 会自动将实体转换为表。

创建存储库

您需要创建保存用户记录的存储库,如下面的列表(在 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 中实现此存储库接口(大小写有所不同,它被称为 userRepository)。

创建控制器

您需要创建一个控制器来处理对应用程序的 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 操作。

创建应用程序类

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://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 守护程序中。要使用 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 将我们的 guide 容器附加到我们的外部容器正在使用的现有网络。您可以在Docker 文档中找到更多信息。

原生映像支持

Spring Boot 还支持编译为原生映像,前提是您的机器上有一个 GraalVM 发行版。要使用原生构建工具创建使用 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 应用程序拥有必要的权限,以便仅更改数据库的数据,而不是结构(模式)。

当您想要更改数据库时

  1. 重新授予权限。

  2. spring.jpa.hibernate.ddl-auto更改为update

  3. 重新运行您的应用程序。

然后重复此处显示的两个命令,以使您的应用程序再次安全用于生产环境。更好的是,使用专用的迁移工具,例如 Flyway 或 Liquibase。

总结

恭喜!您刚刚开发了一个绑定到 MySQL 数据库并已准备好投入生产的 Spring 应用程序!

另请参阅

以下指南可能也有帮助

想要编写新的指南或为现有指南做出贡献?请查看我们的贡献指南

所有指南均以代码的 ASLv2 许可证和文字的署名-非衍生作品创作共用许可证发布。

获取代码