使用 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. 单击生成

  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://: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 守护程序中。要使用 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 分发版。要使用 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 许可,文字内容采用署名-禁止演绎知识共享许可

获取代码