使用 Spring Boot、Hibernate、Postgres、Docker 和 Docker Compose 的 Java CRUD Rest API

2025-06-08

使用 Spring Boot、Hibernate、Postgres、Docker 和 Docker Compose 的 Java CRUD Rest API

让我们用 Java 创建一个 CRUD Rest API,使用:

  • Spring Boot(用 Java 构建 Web 服务器的框架)
  • Hibernate(Java ORM)
  • Postgres(关系数据库)
  • Docker(用于容器化)
  • Docker Compose

如果您更喜欢视频版本:

所有代码均可在 GitHub 存储库中找到(链接在视频描述中):https://youtube.com/live/Nefd6qdpdSI


🏁 简介

以下是我们要创建的应用程序架构图:

增删改查,读取、更新、删除,通过 docker compose 连接到 Spring Boot 应用和 Postgres 服务。使用 Postman 和 Tableplus 进行测试

我们将为基本的 CRUD 操作创建 5 个端点:

  • 创造
  • 阅读全部
  • 阅读一篇
  • 更新
  • 删除

以下是我们正在采取的步骤:

  1. 使用 Spring Boot、Spring Web、Spring Data 和 Hibernate 创建 Java 应用程序。
  2. 使用 Docker Compose 在容器中运行 Postgres 数据库,并使用 TablePlus 对其进行测试。
  3. 将 Java 应用程序 Docker 化,编写 Dockerfile 和 docker-compose.yml 文件来运行应用程序和数据库。
  4. 构建 Java 应用程序,构建 Docker 镜像并使用 Docker Compose 运行容器,然后使用 Postman 进行测试。

我们将提供一步一步的指南,以便您可以跟随。


要求:

  • Java 已安装
  • Maven(或任何其他 Java 构建工具)
  • 任何 Java IDE
  • Docker 安装并运行
  • (可选):Postman 和 Tableplus 可供使用,但任何测试工具都可以使用

☕ 创建一个新的 Java Spring Boot 应用程序

您可以通过多种方式来做到这一点。

如果您使用 VSCode,我建议安装这两个扩展:

  • Java扩展包
  • Spring Initializr Java 支持

如果您有这些扩展,则打开 VS Code 时应该会看到类似的提示。

点击“创建Java项目”

VS Code 左侧带有“创建 JAva 项目”按钮

然后您应该选择如何构建您的 Java 项目。

您可以通过多种方式执行此操作。我们将按以下顺序选择来创建此应用程序:

  • Spring Boot
  • Maven 项目(如果需要,您可以选择 Gradle)
  • Spring Boot 版本 3.0.4(任何 3.0.0 及以上版本都可以)
  • Java 版本 17(任何 11 及以上版本都可以)
  • 选择一个包。我这里选择的是:com.francescociulla.javadocker
  • Artifact Id:live(您可以将其替换为您想要的内容,并相应地更改即将到来的部分)
  • 包装类型:Jar(如果需要,您可以选择war)
  • Java 版本:17
  • Spring Web(处理 HTTP 请求)
  • Spring Data JPA(处理数据库)
  • PostgreSQL 驱动程序

最后选择3个依赖项,按继续:

Spring Initializr 的 VS Code 提示符

现在您应该决定要将该项目生成到一个文件夹中。

就我而言,路径是,c:/workspace但可以自由选择任何路径。

Windows 资源管理器

生成后,您应该会在右下角看到此弹出窗口(或者您可以使用任何 IDE 打开该文件夹)。

生成成功。打开

单击“打开”,您将看到如下内容:

VS Code 及其生成的文件

现在到了重要的部分。如果我们像这样简单地运行应用程序(如果你安装了扩展程序,打开 JavaApplication.java 文件时,右上角应该有一个播放按钮),它会失败。

Java Spring Boot 运行失败:原因:无法确定合适的驱动程序类

这并没有错,因为我们想将此应用程序连接到 Postgres DB,但我们没有运行该 DB。

在这种情况下,我们将通过使用 Docker 容器而不是安装来运行数据库


🐘 运行 Postgres 容器

为此,在根级别创建一个名为“docker-compose.yml”的文件,并按如下方式填充它:



version: '3.9'

services:
  java_db:
    container_name: java_db
    image: postgres:12
    ports:
      - 5432:5432
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_USER: postgres
      POSTGRES_DB: postgres
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata: {}


Enter fullscreen mode Exit fullscreen mode

解释:

  • 我们使用的是 3.9 版的 docker-compose 文件
  • 我们正在创建一个名为“java_db”的服务,具有相同的container_name
  • 我们正在使用图像“postgres:12”(您可以使用任何您想要的版本)
  • 我们将容器的端口 5432 暴露给主机的端口 5432
  • 我们正在设置密码、用户和数据库名称
  • 我们正在创建一个卷来保存数据。

现在,打开终端并输入:



docker compose up -d java_db


Enter fullscreen mode Exit fullscreen mode

等待几秒钟,然后输入:



docker compose logs


Enter fullscreen mode Exit fullscreen mode

如果您有类似的东西,那么“数据库系统已准备好接受连接”您就可以开始了。

日志,最后数据库系统已准备好接受连接

但为了确保万无一失,让我们使用 Tableplus 进行测试

打开 Tableplus 并点击“搜索连接...”旁边的 + 并选择“PostgreSQL”

Tableplus 界面

使用以下内容填充字段:

  • 主机:localhost
  • 端口:5432
  • 用户:postgres
  • 密码:postgres(请勿在生产中使用此密码!)
  • 数据库:postgres

TAbleplus 界面

现在点击右下角的“测试”按钮。如果您收到如下所示的“连接正常”消息,则表示一切正常。

Tableplus 界面

您也可以单击“连接”,您将得到一个空的数据库


🧑‍💻编写 Java Spring Boot 应用程序

是时候创建我们的 Spring Boot 应用程序了。

在主文件夹中,创建一个名为user

在此文件夹中,创建 3 个文件:

  • 用户.java
  • 用户存储库.java
  • 用户控制器.java

您的结构应该类似于这个:

VS Code 资源管理器,其中实时文件夹下方是用户文件夹,下方是 3 个文件

📝 填充User.java文件:



package com.example.live.user;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  @Column(name = "name")
  private String name;

  @Column(name = "email")
  private String email;

  //getters and setters

  public Long getId() {
    return id;
  }

  public void setId(Long 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;
  }
}


Enter fullscreen mode Exit fullscreen mode

解释:

这是一个代表用户实体的 Java 类,与使用 Jakarta Persistence(以前称为 Java Persistence API 或 JPA)的数据库结合使用,用于对象关系映射 (ORM)。

以下是此代码的主要特点:

@Entity 注释用于将此类标记为 JPA 实体,这意味着它可以持久化到数据库表中。

@Table 注解用于指定此实体映射到的数据库表的名称。在本例中,它映射到名为“users”的表。

@Id注解用于指定id字段作为实体的主键。

@GeneratedValue 注解用于指定主键的值应由数据库系统自动生成。在本例中,使用了 GenerationType.AUTO 策略,这意味着数据库将根据所使用的数据库选择适当的策略。

@Column 注解用于指定数据库表中 name 和 email 字段对应的列名,如果没有指定,则默认使用字段名。

该类有一个无参数的构造函数,以及每个字段的 getter 和 setter 方法。
总的来说,该类定义了一个名为“users”的数据库表的模式,该表包含用户 ID、姓名和邮箱地址等列。

@Entity、@Table、@Id 和 @Column 注解用于指定类应如何映射到数据库表,而 getter 和 setter 则提供对对象中数据的访问。

📝 填充UserRepository.java文件:



package com.example.live.user;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

}



Enter fullscreen mode Exit fullscreen mode

代码解释UserRepository.java

这是一个 Java 接口,它使用 Spring Data JPA 为用户实体定义一个存储库。

以下是此代码的主要特点:

文件顶部的导入语句从 Spring Data JPA 库中引入必要的类。

@Repository 注释用于将此接口标记为处理用户实体的数据访问的 Spring bean。

此接口扩展了 JpaRepository 接口。它提供了一组用于处理用户实体的方法,例如保存、删除和查找用户。它还允许对结果进行分页和排序。

UserRepository 接口使用泛型来指定实体类型及其主键的类型。在本例中,它适用于 User 实体和 Long 主键。

总的来说,该接口为在 Spring Data JPA 应用程序中处理用户实体提供了高级抽象。

通过扩展 JpaRepository 接口并定义 User 实体和主键类型,Spring Data JPA 将自动为 JpaRepository 接口中定义的方法生成必要的实现。

这使得开发人员可以专注于定义应用程序的业务逻辑,而不是编写用于数据访问的样板代码。

📝 填充UserController.java如下:



package com.example.live.user;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.PutMapping;

@RestController
@RequestMapping("/api/users")
public class UserController {

  @Autowired
  private UserRepository userRepository;

  @GetMapping
  public List<User> getAllUsers() {
    return userRepository.findAll();
  } 

  @GetMapping("/{id}")
  public User getUserById(@PathVariable Long id) {
    return userRepository.findById(id).get();
  }

  @PostMapping
  public User createUser(@RequestBody User user) {
    return userRepository.save(user);
  }

  @PutMapping("/{id}")
  public User updateUser(@PathVariable Long id, @RequestBody User user) {
    User existingUser = userRepository.findById(id).get();
    existingUser.setName(user.getName());
    existingUser.setEmail(user.getEmail());
    return userRepository.save(existingUser);
  }

  @DeleteMapping("/{id}")
  public String deleteUser(@PathVariable Long id) {
    try {
      userRepository.findById(id).get();
      userRepository.deleteById(id);
      return "User deleted successfully";
    } catch (Exception e) {
      return "User not found";
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

代码解释Usercontroller.java

这是一个 Java 类,它定义了一个 RESTful API,用于使用 Spring Boot 和 Spring Data JPA 对用户实体执行 CRUD(创建、读取、更新、删除)操作。

以下是此代码的主要特点:

文件顶部的导入语句从 Spring Boot 和 Spring Data JPA 库中引入必要的类和注释。

@RestController 注释用于将此类标记为处理 HTTP 请求和响应的 RESTful 控制器。

@RequestMapping 注解用于指定所有 API 端点的基准 URL。在本例中,它被设置为“/api/users”。

@Autowired 注解用于将 UserRepository 接口的实例注入到此类中。

此类中定义了几种端点方法,每种方法都映射到特定的 HTTP 方法(GET、POST、PUT、DELETE)和 URL 路径。

getAllUsers() 是一种 GET 方法,通过调用 UserRepository 上的 findAll() 方法返回数据库中所有用户的列表。

getUserById(Long id) 是一种 GET 方法,它接受 URL 路径中的 ID 参数,并通过调用 UserRepository 上的 findById(Long id) 方法返回具有该 ID 的单个用户。

createUser(User user) 是一种 POST 方法,它在请求正文中采用新用户的 JSON 表示形式,并通过调用 UserRepository 上的 save(User user) 方法将其保存到数据库。

updateUser(Long id, User user) 是一个 PUT 方法,它在 URL 路径中接收一个 ID 参数,并在请求正文中接收更新后用户的 JSON 表示。该方法首先使用 findById(Long id) 从数据库中检索用户,然后将其属性设置为更新后用户的属性,最后使用 UserRepository 上的 save(User user) 方法保存更新后的用户,并根据给定的 ID 更新用户。

deleteUser(Long id) 是一种 DELETE 方法,它接受 URL 路径中的 ID 参数,并通过调用 UserRepository 上的 deleteById(Long id) 方法从数据库中删除具有该 ID 的用户。

总的来说,此类提供了一套完整的 RESTful API 端点,用于管理 Spring Boot 和 Spring Data JPA 应用程序中的用户实体。

现在是时候完成配置部分了,然后我们就完成了

application.properties 文件是 Spring Boot 应用程序使用的配置文件。它定义了配置应用程序数据源和 Hibernate(用于数据库持久化的 ORM(对象关系映射)框架)的属性。

📝 application.properties 文件

该文件位于resources文件夹中。

填充application.properties文件:



spring.datasource.url=${DATABASE_URL}
spring.datasource.username=${DATABASE_USERNAME}
spring.datasource.password=${DATABASE_PASSWORD}
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect


Enter fullscreen mode Exit fullscreen mode

解释:

spring.datasource.url此属性设置应用程序将连接到的数据库的 URL。该值使用 ${DATABASE_URL} 占位符设置,该占位符将在 docker-compose.yml 文件中设置。

spring.datasource.username此属性设置连接数据库时使用的用户名。该值使用 ${DATABASE_USERNAME} 占位符设置,该占位符将在 docker-compose.yml 文件中设置。

spring.datasource.password此属性设置连接数据库时使用的密码。该值使用 ${DATABASE_PASSWORD} 占位符设置,该占位符将在 docker-compose.yml 文件中设置。

spring.jpa.hibernate.ddl-auto此属性设置 Hibernate 生成和更新数据库模式的策略。在本例中,该值设置为 update,这意味着 Hibernate 将根据应用程序中的实体类自动创建表并根据需要更新模式。

spring.jpa.properties.hibernate.dialect此属性设置用于与数据库通信的 Hibernate 方言。在本例中,该值设置为 org.hibernate.dialect.PostgreSQLDialect,这是 PostgreSQL 数据库的方言。

总的来说,这个application.properties文件用于配置连接到PostgreSQL数据库的Spring Boot应用程序的数据源和ORM框架。


🐳 Docker化

现在,要将数据库连接到 Spring App,我们需要先将应用程序 Docker 化,然后使用 Docker Compose 运行它

此 Dockerfile 用于为打包为 JAR 文件的 Java 应用程序创建 Docker 镜像。

在应用程序根级别,创建一个名为Dockerfile(大写“D”)的文件并像这样填充它:



FROM openjdk:17-jdk-alpine

COPY target/live-0.0.1-SNAPSHOT.jar app-1.0.0.jar

ENTRYPOINT [ "java", "-jar", "app-1.0.0.jar" ]


Enter fullscreen mode Exit fullscreen mode

代码解释Dockerfile

FROM openjdk:17-jdk-alpine此行指定用于构建 Docker 镜像的基础镜像。在本例中,它使用 openjdk:17-jdk-alpine 镜像,它是 OpenJDK 17 镜像的轻量级版本,针对在容器化环境中运行 Java 应用程序进行了优化。

COPY target/live-0.0.1-SNAPSHOT.jar app-1.0.0.jar此行将 Java 应用程序的 JAR 文件从本地文件系统的 target 目录复制到 Docker 镜像的根目录,并将其重命名为 app-1.0.0.jar。此操作假设该 JAR 文件已在本地文件系统的 target 目录中构建并打包。如果您为项目使用了不同的 artifactId,则需要相应地更改 JAR 文件的名称。

ENTRYPOINT [ "java", "-jar", "app-1.0.0.jar" ]此行设置从 Docker 镜像启动容器时运行的命令。在本例中,它指定使用 -jar 选项执行 java 命令,并且要运行的 JAR 文件是 app-1.0.0.jar。


Docker Compose

java_app我们在之前创建的 docker-compose.yml 文件中添加该服务。



version: '3.9'

services:
  #new service (java_app)
  java_app:
    container_name: java_app
    image: francescoxx/java_app:1.0.0
    build: .
    ports:
      - 8080:8080
    environment:
      - DATABASE_URL=jdbc:postgresql://java_db:5432/postgres
      - DATABASE_USERNAME=postgres
      - DATABASE_PASSWORD=postgres
    depends_on:
      - java_db

  #old service (postgres)
  java_db:
    container_name: java_db
    image: postgres:12
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: postgres
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata: {}



Enter fullscreen mode Exit fullscreen mode

服务说明java_app

container_name是容器的自定义名称。这是可选的,但我建议使用它,因为它可以更轻松地在正在运行的容器列表中找到该容器。

image是我们要分配的镜像标签(名称)。我建议使用你的 dockerhub 账户用户名,而不是“francescoxx”(这是我的)。

build是 Dockerfile 的构建路径。在本例中,Dockerfile 和 docker-compose.file 位于同一目录中,因此我们只需使用点号,.表示“此处,在此文件夹中”。

ports是我们要公开的端口列表。在本例中,我们将公开 Java 应用容器的 8080 端口。格式为“host_port:container_port”。

environment是我们要使用的环境变量列表。在本例中,我们有三个环境变量,用于连接数据库,包含用户名和密码(当然,在生产环境中请使用更安全的密码!)。另请注意,连接的对象是“java_db”。如果容器位于同一网络上,它们可以通过容器名称相互找到。这就是为什么定义容器名称如此重要,至少对于数据库容器而言是如此。

depends_on是在运行 Postgres 容器之后再运行此容器。但请注意,这样做只会等待容器启动,而不是数据库应用程序启动,因此第一次尝试可能会失败(我们需要一个叫做“健康检查”的东西来正确执行此操作)。


🧱 创建 Jar 文件

我们需要创建 Java 应用程序的 jar 文件,以便将其复制到 Docker 镜像中。

要创建 jar 文件,请输入:



mvn clean package -DskipTests


Enter fullscreen mode Exit fullscreen mode

注意:我们在这里跳过测试,因为否则构建过程将会失败!

原因是测试试图连接数据库,但我们定义的环境变量位于应用程序逻辑之外。

现在使用以下命令构建 Docker 镜像:



docker compose build


Enter fullscreen mode Exit fullscreen mode

如果由于目标文件不存在而出现错误,请检查目标文件夹中 Java 文件的名称


👟 运行 Java 应用程序

要运行 Java 应用程序,只需输入:



docker compose up java_app


Enter fullscreen mode Exit fullscreen mode

如果您的输出类似于以下内容,那么您就可以开始了:

JavaSpring 启动应用程序日志

为确保万无一失,在另一个终端上输入



docker ps -a


Enter fullscreen mode Exit fullscreen mode

您应该看到类似这样的内容:

2 个容器启动并运行

现在是时候测试我们的应用程序了。


🔍 测试我们的应用程序

在测试我们的应用程序之前,让我们回到 Tableplus 并使用应用程序右上角的🔁按钮刷新(或关闭并重新打开连接)。

现在数据库中有一张表了。这是因为我们用 @Entity 注解了 User 类,并且将模型与数据库同步了(我们在 application.properties 文件中定义了一行代码来实现这一点,参见 application.properties 文件的第 4 行)。

TablePlus 和用户表

现在让我们用 Postman 测试这个 CRUD API 应用程序

📝 测试应用程序

由于我们没有测试端点,我们只能向发出 GET 请求localhost:8080/api/users

响应应该是一个空数组

Postman GET 请求

📝 创建用户

现在让我们创建一个用户,并发出一个 POST 请求,localhost:8080/api/users请求主体如下:

Postman POST 请求

我们再做一个:

Postman POST 请求

第三个:

Postman POST 请求

📝 获取所有用户

现在我们创建了 3 个用户,让我们获取所有用户,向localhost:8080/api/users

Postman GET 请求

📝 获取一个用户

要获取单个用户,您只需发出 GET 请求,并在 URL 末尾附加我们想要的用户编号

例如,要获取用户 #3,您可以发出 GET 请求localhost:8080/api/user/3

Postman GET 请求

📝 更新一位用户

要更新现有用户,您需要提供新的主体和您想要修改的用户。

例如,要更新用户#2,您可以localhost:8080/api/users/2通过提供新的主体来发出 PUT 请求:

Postman PUT 请求

📝 删除一个用户

要删除一个用户,请发出 DELETE 请求并附加要删除的用户的 ID。

例如,要删除用户 #1,请发出 DELETE 请求localhost:8080/api/users/1

Postman DELETE 请求

请注意,如果我们尝试删除不存在的用户,我们会处理错误,例如:

Postman DELETE 请求

作为最后的测试,让我们用 TablePlus 再次检查

TablePlus


🏁 结论

我们成功了!我们用 Java 构建了一个 CRUD REST API,使用:

  • Spring Boot(依赖项:Spring Web、Spring Data JPA、PostgreSQL 连接器)
  • 休眠
  • Postgres
  • Docker
  • Docker 组成。

这只是一个例子,但您可以用它作为构建自己的应用程序的起点。

如果您更喜欢视频版本:

所有代码均可在 GitHub 存储库中找到(链接在视频描述中):https://youtube.com/live/Nefd6qdpdSI

就这样。

如果您有任何疑问,请在下面发表评论。

弗朗西斯科

鏂囩珷鏉ユ簮锛�https://dev.to/francescoxx/java-crud-rest-api-using-spring-boot-hibernate-postgres-docker-and-docker-compose-5cln
PREV
Rust fullstack web app! WASM + YEW + ROCKET
NEXT
Go + TypeScript 全栈 Web 应用程序,带有 nextjs、PostgreSQL 和 Docker