1、概览 {#1概览}
本文将带你了解如何使用 Docker Compose 来运行 Spring Boot 和 PostgreSQL。
2、创建 Spring Boot 应用 {#2创建-spring-boot-应用}
从 Spring Initializer 创建 Spring Boot 项目,添加 PostgreSQL 驱动和 Spring Data JPA 依赖。下载生成的 ZIP 文件并解压到文件夹后,就可以运行应用了:
./mvnw spring-boot:run
应用启动失败,因为连接数据库失败:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
3、Dockerfile {#3dockerfile}
在使用 Docker Compose 启动 PostgreSQL 之前,需要将 Spring Boot 应用转化为 Docker 镜像。第一步是将应用打包为 JAR 文件:
./mvnw clean package -DskipTests
如上,首先清理之前的构建,然后再打包应用。而且跳过测试,因为没有 PostgreSQL,测试会失败。
现在,在 target
目录中有一个应用 JAR 文件。该文件的名称中包含了项目名称和版本号,并以 -SNAPSHOT.jar
结尾:docker-spring-boot-postgres-0.0.1-SNAPSHOT.jar
。
创建新的 src/main/docker
目录。然后,将应用 JAR 文件复制到该目录:
cp target/docker-spring-boot-postgres-0.0.1-SNAPSHOT.jar src/main/docker
最后,在同一目录下创建如下 Dockerfile
:
FROM adoptopenjdk:11-jre-hotspot
ARG JAR_FILE=*.jar
COPY ${JAR_FILE} application.jar
ENTRYPOINT ["java", "-jar", "application.jar"]
该文件描述了 Docker 应如何运行 Spring Boot 应用。它使用 AdoptOpenJDK 中的 Java 11,并将应用 JAR 文件复制到 application.jar
。然后运行该 JAR 文件来启动 Spring Boot 应用。
4、Docker Compose 文件 {#4docker-compose-文件}
现在,编写 Docker Compose 文件 docker-compose.yml
,并将其保存在 src/main/docker
中:
version: '2'
services:
app:
image: 'docker-spring-boot-postgres:latest'
build:
context: .
container_name: app
depends_on:
- db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/compose-postgres
- SPRING_DATASOURCE_USERNAME=compose-postgres
- SPRING_DATASOURCE_PASSWORD=compose-postgres
- SPRING_JPA_HIBERNATE_DDL_AUTO=update
db:
image: 'postgres:13.1-alpine'
container_name: db
environment:
- POSTGRES_USER=compose-postgres
- POSTGRES_PASSWORD=compose-postgres
应用名为 app
。它是两个服务中的第一个(第 4-15 行):
- Spring Boot 的 Docker 镜像名为
docker-spring-boot-postgres:late
(第 5 行)。Docker 会根据当前目录中的Dockerfile
构建该镜像(第 6-7 行)。 - 容器名称是
app
(第 8 行)。它依赖于db
服务(第 10 行)。这就是它在db
容器之后启动的原因。 - 应用使用
db
PostgreSQL 容器作为数据源(第 12 行)。数据库名称、用户名和密码都是compose-postgres
(第 12-14 行)。 - Hibernate 会自动创建或更新所需的数据库表(第 15 行)。
PostgreSQL 数据库名为 db
,是第二个服务(第 17-22 行):
- 使用 PostgreSQL 13.1(第 18 行)。
- 容器名称为
db
(第 19 行)。 - 用户名和密码都是
compose-postgres
(第 21-22 行)。
5、使用 Docker Compose 运行 {#5使用-docker-compose-运行}
使用 Docker Compose 运行 Spring Boot 应用和 PostgreSQL:
docker-compose up
首先,它会为 Spring Boot 应用构建 Docker Image。接着,启动 PostgreSQL 容器。最后,启动应用 Docker 镜像。
这一次,应用运行正常:
Starting DemoApplication v0.0.1-SNAPSHOT using Java 11.0.9 on f94e79a2c9fc with PID 1 (/application.jar started by root in /)
[...]
Finished Spring Data repository scanning in 28 ms. Found 0 JPA repository interfaces.
[...]
Started DemoApplication in 4.751 seconds (JVM running for 6.512)
如你所见,Spring Data 没有找到 JPA Repository 接口,因为我们还没创建。
按下 Ctrl-C
来停止所有容器,然后停止 Docker Compose:
docker-compose down
6、创建 Customer Entity 和 Repository {#6创建-customer-entity-和-repository}
为了在应用中使用 PostgreSQL 数据库,创建一个简单的 Customer
实体:
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue
private long id;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name", nullable = false)
private String lastName;
// get / set 等方法省略
}
Customer
有一个自动生成的 id
属性和两个必选属性:firstName
和 lastName
。
然后,创建其对应的 Repository 接口:
public interface CustomerRepository extends JpaRepository<Customer, Long> { }
通过继承 JpaRepository
,获得用于创建和查询 Customer
实体的方法。
最后,在应用中使用这些方法:
@SpringBootApplication
public class DemoApplication {
@Autowired
private CustomerRepository repository;
@EventListener(ApplicationReadyEvent.class)
public void runAfterStartup() {
List allCustomers = this.repository.findAll();
logger.info("Number of customers: " + allCustomers.size());
Customer newCustomer = new Customer();
newCustomer.setFirstName("John");
newCustomer.setLastName("Doe");
logger.info("Saving new customer...");
this.repository.save(newCustomer);
allCustomers = this.repository.findAll();
logger.info("Number of customers: " + allCustomers.size());
}
}
- 通过字段注入
CustomerRepository
依赖。 - 查询现有
Customer
的数量,结果为 0。 - 然后,创建并保存一个
Customer
。 - 再次查询现有
Customer
,就会检索到刚刚创建的Customer
。
7、再次运行 Docker Compose {#7再次运行-docker-compose}
要运行更新后的 Spring Boot 应用,需要先重新构建它。因此,要在项目根目录下再次执行这些命令:
./mvnw clean package -DskipTests
cp target/docker-spring-boot-postgres-0.0.1-SNAPSHOT.jar src/main/docker
如何用更新后的应用 JAR 文件重建 Docker 镜像?最好的办法是删除在 docker-compose.yml
中指定名称的现有 Docker 镜像。这样,当下一次启动 Docker Compose 文件时,Docker 就会重新构建镜像:
cd src/main/docker
docker-compose down
docker rmi docker-spring-boot-postgres:latest
docker-compose up
因此,在停止容器后,删除应用的 Docker 镜像。然后,再次启动 Docker Compose,重建应用镜像。
应用输出的日志如下:
Finished Spring Data repository scanning in 180 ms. Found 1 JPA repository interfaces.
[...]
Number of customers: 0
Saving new customer...
Number of customers: 1
如上,首先检索 Repository 中的 Customer
数量,结果为 0,然后创建新的 Customer
实体,保存。最后,再次检索,结果为 1。
符合预期。
8、总结 {#8总结}
本文介绍了如何在 Docker Compose 中运行使用 PostgreSQL 的 Spring Boot 应用。
Ref:https://www.baeldung.com/spring-boot-postgresql-docker