Chúng ta thường sử dụng Docker Compose để có thể chạy nhiều ứng dụng cùng với database hoặc với các service khác mà ứng dụng đang sử dụng, cùng một lúc, sử dụng nhiều Docker container. Từ phiên bản 3.1 trở đi, Spring Boot cũng hỗ trợ Docker Compose giúp cho việc phát triển các ứng dụng Spring Boot dễ dàng hơn rất nhiều. Cụ thể như thế nào? Chúng ta hãy cùng nhau tìm hiểu trong bài viết này các bạn nhé!
Đầu tiên, mình sẽ tạo mới một Spring Boot project với Spring Data JPA, PostgreSQL Driver và Docker Compose Support:
để làm ví dụ.
Như các bạn thấy, khi mình tạo Spring Boot project với Docker Compose Support, một tập tin compose.yaml cũng được tạo cùng.
Kiểm tra tập tin compose.yaml này, các bạn sẽ thấy một service postgres đã được khai báo sẵn, như sau:
1 2 3 4 5 6 7 8 9 |
services: postgres: image: 'postgres:latest' environment: - 'POSTGRES_DB=mydatabase' - 'POSTGRES_PASSWORD=secret' - 'POSTGRES_USER=myuser' ports: - '5432' |
Đó là bởi vì mình khai báo sử dụng PostgreSQL Driver đó các bạn!
Các bạn cần sửa phần ports một xí để expose port 5432 của PostgreSQL database ra bên ngoài, như sau:
1 2 3 4 5 6 7 8 9 10 11 |
services: postgres: image: 'postgres:latest' volumes: - ./src/main/resources/init.sql:/docker-entrypoint-initdb.d/init.sql environment: - 'POSTGRES_DB=mydatabase' - 'POSTGRES_PASSWORD=secret' - 'POSTGRES_USER=myuser' ports: - '5432:5432' |
Lúc này, nếu các bạn chạy ứng dụng Spring Boot này, các bạn sẽ thấy Spring Boot tự động pull Docker Image của PostgreSQL về, kết quả như sau:
Vì hiện tại project của mình chưa có gì, nên sau khi pull Docker Image của PostgreSQL về, ứng dụng của mình sẽ tự động shutdown.
Để làm ví dụ cho project này, mình sẽ connect đến PostgreSQL database để lấy thông tin sinh viên.
Table student mình sẽ định nghĩa như sau:
1 2 3 4 5 6 7 |
CREATE TABLE student ( id INT PRIMARY KEY, name VARCHAR(100) ); INSERT INTO student(id, name) VALUES (1, 'Khanh'); |
Để đơn giản, mình sẽ lưu DDL của table student vào tập tin db.sql ở thư mục src/main/resources của project và chỉnh sửa postgres service trong tập tin compose.yaml để nó tự động khởi tạo table student khi chạy Docker Container lên cho mình, như sau:
1 2 3 4 5 6 7 8 9 10 11 |
services: postgres: image: 'postgres:latest' volumes: - ./src/main/resources/db.sql:/docker-entrypoint-initdb.d/init.sql environment: - 'POSTGRES_DB=mydatabase' - 'POSTGRES_PASSWORD=secret' - 'POSTGRES_USER=myuser' ports: - '5432:5432' |
Bây giờ, mình sẽ khai báo thông tin database cho ứng dụng của mình trong tập tin application.properties như sau:
1 2 3 |
spring.datasource.url=jdbc:postgresql://localhost:5432/mydatabase spring.datasource.username=myuser spring.datasource.password=secret |
Mình cũng sẽ khai báo entity Student và class Repository cho entity này như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.huongdanjava.springboot; import jakarta.persistence.Entity; import jakarta.persistence.Id; @Entity public class Student { private @Id long id; private String name; public Student() { } 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; } } |
và:
1 2 3 4 5 6 7 |
package com.huongdanjava.springboot; import org.springframework.data.jpa.repository.JpaRepository; public interface StudentRepository extends JpaRepository<Student, Long> { } |
Chạy ứng dụng với CommandLineRunner của Spring Boot:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package com.huongdanjava.springboot; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringBootDockerComposeSupportApplication { @Autowired private StudentRepository studentRepository; public static void main(String[] args) { SpringApplication.run(SpringBootDockerComposeSupportApplication.class, args); } @Bean CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { studentRepository.findAll().forEach(s -> System.out.println(s.getName())); }; } } |
các bạn sẽ thấy kết quả như sau:
Như các bạn thấy, ứng dụng của mình đã connect tới PostgreSQL Docker Container được Spring Boot chạy lên khi mình chạy ứng dụng Spring Boot.
Một hỗ trợ khác của Spring Boot cho Docker Compose đó là chúng ta không cần phải khai báo thông tin PostgreSQL trong tập tin application.properties luôn!
Thông tin về connection tới PostgreSQL database sẽ được chứa trong bean của class ConnectionDetails.
Các bạn có thể xoá nội dung cấu hình database mà chúng ta đã khai báo ở trên trong tập tin application.properties và chạy lại ứng dụng. Các bạn cũng sẽ thấy kết quả tương tự.
Thử inject bean của class ConnectionDetails và in thông tin của PostgreSQL database:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package com.huongdanjava.springboot; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails; import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringBootDockerComposeSupportApplication { @Autowired private StudentRepository studentRepository; @Autowired @Qualifier("jdbcConnectionDetailsForSpringBootDockerComposeSupportPostgres1") private ConnectionDetails connectionDetails; public static void main(String[] args) { SpringApplication.run(SpringBootDockerComposeSupportApplication.class, args); } @Bean CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println(((JdbcConnectionDetails) connectionDetails).getJdbcUrl()); }; } } |
các bạn sẽ thấy kết quả như sau:
Nếu các bạn ko expose port của PostgreSQL ra bên ngoài mà chỉ khai báo như sau trong tập tin compose.yaml:
1 2 3 4 5 6 7 8 9 10 11 |
services: postgres: image: 'postgres:latest' volumes: - ./src/main/resources/db.sql:/docker-entrypoint-initdb.d/db.sql environment: - 'POSTGRES_DB=mydatabase' - 'POSTGRES_PASSWORD=secret' - 'POSTGRES_USER=myuser' ports: - '5432' |
PostgreSQL database sẽ được expose ra bên ngoài sử dụng random port và ứng dụng của chúng ta cũng có thể connect tới port này luôn. Cho ví dụ của mình, nếu chạy lại, các bạn sẽ thấy kết quả tương tự như mình như sau:
Điều này rất hữu ích nếu các bạn đang phát triển các ứng dụng microservices, mỗi service sử dụng một database riêng. Random port sẽ giúp chúng ta tránh bị conflict port đó các bạn!