+1

Tản mạn về Liquibase và cách tích hợp vào ứng dụng Spring Boot

Source: https://www.tuanh.net/blog/spring/stories-about-liquibase-and-how-to-integrate-it-into-spring-boot-applications

Bạn đang tìm kiếm một công cụ mạnh mẽ để tối ưu hóa quản lý cơ sở dữ liệu của mình? Liquibase là câu trả lời của bạn. Công cụ mã nguồn mở này đơn giản hóa việc theo dõi và triển khai các thay đổi cơ sở dữ liệu với kiểm soát phiên bản và tự động hóa. Blog của chúng tôi cung cấp các hướng dẫn chi tiết về Liquibase, nhấn mạnh lợi ích của nó và cách tích hợp nó vào quy trình làm việc của bạn. Đắm mình và tìm hiểu cách Liquibase có thể giúp bạn duy trì tính nhất quán của cơ sở dữ liệu một cách dễ dàng.

1. Một câu chuyện buồn...

Dan là một người cha đơn thân nên anh ấy đảm nhiệm tất cả việc nhà. Để đảm bảo mọi thứ diễn ra suôn sẻ và không bị trễ giờ làm, Dan đã lập ra một lịch trình buổi tối như sau:

8 giờ tối: Ăn tối.

9 giờ tối: Chuẩn bị bữa sáng cho ngày hôm sau, mất khoảng 1 tiếng.

10 giờ tối: Đi ngủ để thức dậy đúng giờ vào sáng hôm sau.

6 giờ sáng hôm sau: Thức dậy, pha sữa cho con và nấu bữa sáng.

8 giờ 30 sáng hôm sau: Đến công ty đúng giờ.

Đây là thói quen hàng ngày của Dan.

image.png

Tuy nhiên, mọi thứ đã khác đi vào ngày hôm đó. Dan:

image.png

8 giờ tối: Ăn tối.

9 giờ tối: Chuẩn bị bữa sáng cho ngày hôm sau, mất 1 tiếng.

10 giờ tối: Dan tình cờ phát hiện ra một trò chơi thú vị và quyết định chơi đến 2 giờ sáng.

2 giờ sáng hôm sau: Dan đi ngủ.

7 giờ sáng hôm sau: Dan thức dậy, vội vàng pha sữa cho con và nấu bữa sáng.

9 giờ sáng hôm sau: Dan đến công ty muộn và bị sếp mắng vì đi muộn.

Sự thay đổi kế hoạch này khiến Dan gặp phải những vấn đề không mong muốn trong công việc.

Dan đã xây dựng một lịch trình hàng ngày hoàn hảo như một tác phẩm nghệ thuật. Nhưng rồi, đêm đó, anh ta phát hiện ra một trò chơi mới. Đó là một cú sốc đối với kế hoạch của anh ta, và mọi thứ sụp đổ như những quân cờ Domino. Sáng hôm sau, sếp anh ta không vui khi thấy anh ta đi muộn. Giá như có một loại cảnh báo nào đó để nhắc anh ta dừng chơi và đi ngủ, anh ta đã có thể giữ được lịch trình hoàn hảo của mình.

Vậy, liệu có thể tạo ra một lịch trình và nhận được cảnh báo khi bạn sắp phá vỡ nó không? Điều này có thực sự hiệu quả không?

2. Liên quan đến quản lý cơ sở dữ liệu

Dan có một kế hoạch hàng ngày rõ ràng, tương tự như việc quản lý cơ sở dữ liệu cần một kế hoạch chi tiết cho các thay đổi cần thực hiện. Việc Dan bị phân tâm bởi trò chơi và lệch khỏi kế hoạch có thể được so sánh với việc không kiểm soát đúng các thay đổi cơ sở dữ liệu, dẫn đến lỗi hoặc mâu thuẫn trong hệ thống. Hệ thống cảnh báo nhắc nhở Dan đi ngủ đúng giờ có thể được so sánh với các công cụ và phương pháp trong quản lý cơ sở dữ liệu để giám sát và kiểm soát các thay đổi. Trong quản lý cơ sở dữ liệu, duy trì tính toàn vẹn dữ liệu và ổn định hệ thống là rất quan trọng. Tương tự, Dan cần tuân thủ kế hoạch của mình để duy trì sự ổn định trong cuộc sống cá nhân và công việc. Khi Dan không theo kế hoạch, kết quả là sự không nhất quán (đi làm muộn). Trong một cơ sở dữ liệu, sự không nhất quán do các thay đổi không được kiểm soát có thể dẫn đến lỗi hoặc dữ liệu sai.

3. Liên quan đến Liquibase

Liquibase là một công cụ quản lý cơ sở dữ liệu hỗ trợ quản lý các thay đổi sơ đồ cơ sở dữ liệu. Nó sử dụng các tệp thay đổi, thường được gọi là nhật ký thay đổi, để theo dõi và ghi lại các sửa đổi theo thời gian. Bằng cách áp dụng một phương pháp có cấu trúc, Liquibase đảm bảo rằng các thay đổi cơ sở dữ liệu được áp dụng tuần tự và nhất quán, giảm thiểu rủi ro lỗi.

Khái niệm này có thể được so sánh với thói quen hàng ngày của Dan. Giống như Dan có một kế hoạch hàng ngày để duy trì trật tự và hiệu quả, Liquibase cung cấp một khung làm việc có cấu trúc cho quản lý cơ sở dữ liệu. Cả Dan và Liquibase đều tuân theo một kế hoạch được xác định trước. Bất kỳ thay đổi không theo kế hoạch nào, cho dù đó là Dan chơi game hay một sửa đổi cơ sở dữ liệu bất ngờ, đều có thể phá vỡ trật tự đã thiết lập. Tuân theo kế hoạch hàng ngày giúp Dan duy trì sự ổn định trong cuộc sống cá nhân và chuyên nghiệp. Tương tự, Liquibase đảm bảo tính toàn vẹn của cơ sở dữ liệu bằng cách theo dõi và kiểm soát các thay đổi. Một báo thức để nhắc nhở Dan đi ngủ đúng giờ đóng vai trò như một cơ chế kiểm soát. Tương tự, Liquibase cung cấp các công cụ để theo dõi và kiểm soát các thay đổi, ngăn ngừa sự cố và đảm bảo tính nhất quán của dữ liệu. Các sự kiện bất ngờ như khám phá một trò chơi mới có thể phá vỡ thói quen của Dan. Trong ngữ cảnh cơ sở dữ liệu, Liquibase cung cấp các cơ chế để xử lý các thay đổi hoặc lỗi không lường trước, cho phép khôi phục hoặc điều chỉnh. Kế hoạch hàng ngày của Dan cho phép anh ấy theo dõi và điều chỉnh thói quen để cải thiện. Liquibase cung cấp khả năng khôi phục, cho phép người dùng quay trở lại trạng thái cơ sở dữ liệu trước đó nếu cần. Về bản chất, Liquibase cung cấp một phương pháp tiếp cận hệ thống cho quản lý cơ sở dữ liệu, giống như một kế hoạch hàng ngày được cấu trúc tốt. Bằng cách áp đặt trật tự, theo dõi các thay đổi và cung cấp các cơ chế kiểm soát và khôi phục, Liquibase giúp đảm bảo độ tin cậy và tính toàn vẹn của cơ sở dữ liệu.

4. Cách tích hợp vào ứng dụng Spring Boot

Thêm phần phụ thuộc sau vào tệp pom.xml.

<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
    <version>4.3.1</version>
</dependency>

Sau đó, tạo một tệp cấu hình

@Configuration
@EnableConfigurationProperties({ LiquibaseProperties.class})
public class LiquibaseConfiguration {

    private final Logger log = LoggerFactory.getLogger(LiquibaseConfiguration.class);

    private final Environment env;

    public LiquibaseConfiguration(Environment env) {
        this.env = env;
    }

    @Bean
    public SpringLiquibase liquibase(final DataSource dataSource) {
        SpringLiquibase liquibase = new SpringLiquibase();
        liquibase.setChangeLog("classpath:databaseChangeLog.xml");
        liquibase.setDataSource(dataSource);
        return liquibase;
    }
}

Sau đó, tạo một file databaseChangeLog.xml trong thư mục resources để lưu trữ các changeset. Mỗi changeset đại diện cho một lịch trình làm việc của Dan, như đã thảo luận ở trên.

<databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext
   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd
   http://www.liquibase.org/xml/ns/dbchangelog
   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">

    <include file="databaseChangeLogStandard.xml"></include>

    <changeSet author="blog" id="DEV-1">
        <createTable tableName="like">
            <column name="id" type="varchar(100)">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="article_id" type="varchar(255)"/>
            <column name="create_by" type="varchar(255)"/>
            <column name="create_at" type="TIMESTAMP" defaultValueDate="CURRENT_TIMESTAMP"/>
            <column name="modified_by" type="varchar(255)" defaultValue="0"/>
            <column name="modified_at" type="TIMESTAMP"/>
        </createTable>
    </changeSet>
    ......
</databaseChangeLog>

Bạn có hai lựa chọn để quản lý nội dung XML: tạo nhiều tệp XML nhỏ và bao gồm chúng trong một tệp chính, hoặc viết nội dung trực tiếp vào tệp chính. Sau đó, bạn sẽ nhận được các thông báo đăng nhập như thế này để xác nhận thành công.

24-08-08 22:27:09 [main] INFO  liquibase.lockservice - Successfully acquired change log lock
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Creating database history table with name: blog.DATABASECHANGELOG
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Reading from blog.DATABASECHANGELOG
2024-08-08 22:27:09 [main] INFO  liquibase.lockservice - Successfully released change log lock
2024-08-08 22:27:09 [main] INFO  liquibase.lockservice - Successfully acquired change log lock
Skipping auto-registration
2024-08-08 22:27:09 [main] WARN  liquibase.hub - Skipping auto-registration
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table article created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Custom SQL executed
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table user_entity created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table comment created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table rate_tags created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table tokens created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - ChangeSet databaseChangeLogStandard.xml::init-database::blog ran successfully in 216ms
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table like created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - ChangeSet databaseChangeLog.xml::DEV-1::blog ran successfully in 13ms
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table pdf_file created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table book_subscriber created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Unique constraint added to book_subscriber(subscriber_id, file_id)
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - ChangeSet databaseChangeLog.xml::DEV-2::blog ran successfully in 56ms
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Table email_preference created
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - ChangeSet databaseChangeLog.xml::DEV-3::blog ran successfully in 23ms
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - Columns num_like(int) added to article
2024-08-08 22:27:09 [main] INFO  liquibase.changelog - ChangeSet databaseChangeLog.xml::DEV-4::blog ran successfully in 146ms
2024-08-08 22:27:09 [main] INFO  liquibase.lockservice - Successfully released change log lock

Khi bạn cố gắng thay đổi một tập tin thay đổi (changset) đã tồn tại, bạn sẽ không thể khởi động ứng dụng và sẽ nhận được lỗi sau.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tokenRepository' defined in com.blog.repositories.TokenRepository defined in @EnableJpaRepositories declared on DatabaseConfiguration: Cannot create inner bean '(inner bean)#32a4ecbe' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#32a4ecbe': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [com/blog/configuration/LiquibaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.ValidationFailedException: Validation Failed:
     1 change sets check sum
          databaseChangeLog.xml::DEV-1::blog was: 8:a65f5d884bb0759d939028af696a66c6 but is now: 8:73481352a034400de829cad6477b8306

	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:389)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:134)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1707)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1452)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
	... 54 common frames omitted

5. Cấu trúc bảng của Liquibase

Khi Liquibase hoàn thành chạy, nó tạo ra hai bảng đặc biệt tên là databasechangelog và databasechangeloglock, ngoài các bảng bạn đã định nghĩa trong changeset của mình.

image.png

Bảng DATABASECHANGELOG trong Liquibase đóng vai trò quan trọng trong việc theo dõi và quản lý các thay đổi sơ đồ cơ sở dữ liệu. Cụ thể, bảng này phục vụ các chức năng sau:

  • Ghi lại lịch sử thay đổi: Bảng lưu trữ thông tin về các thay đổi sơ đồ cơ sở dữ liệu đã được áp dụng, bao gồm các sửa đổi như tạo bảng, thay đổi cột, thêm chỉ mục, v.v.
  • Theo dõi trạng thái: Bảng này giúp Liquibase theo dõi các thay đổi đã được áp dụng, ngăn chặn việc áp dụng cùng một thay đổi hai lần và đảm bảo tính nhất quán trong quá trình triển khai.
  • Quản lý phiên bản: Bảng ghi lại các thuộc tính như ID, AUTHOR, FILENAME và DATEEXECUTED, hỗ trợ quản lý phiên bản và theo dõi thời gian của các thay đổi.
  • Khôi phục và hoàn tác: Khi cần khôi phục cơ sở dữ liệu về trạng thái trước đó, Liquibase có thể sử dụng thông tin trong bảng DATABASECHANGELOG để xác định các thay đổi cần phải hoàn tác.

image.png

Bảng DATABASECHANGELOGLOCK trong Liquibase đảm bảo rằng chỉ có một tiến trình Liquibase có thể sửa đổi cơ sở dữ liệu tại một thời điểm.

  • Khóa thay đổi cơ sở dữ liệu: Bảng này ngăn chặn việc sửa đổi đồng thời cơ sở dữ liệu bởi một phiên bản Liquibase. Nó hoạt động như một cơ chế khóa để đảm bảo tính toàn vẹn dữ liệu.
  • Bảo vệ tính toàn vẹn: Một phiên bản Liquibase sẽ có được một khóa độc quyền trên cơ sở dữ liệu trước khi thực hiện thay đổi, ngăn chặn các phiên bản khác can thiệp.
  • Quản lý trạng thái khóa: Bảng theo dõi trạng thái khóa, bao gồm người giữ khóa và dấu thời gian.

image.png

Nếu locker = 1

2024-08-08 22:46:06 [main] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
2024-08-08 22:46:06 [main] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
2024-08-08 22:46:06 [main] INFO  liquibase.lockservice - Waiting for changelog lock....

Do đó, chúng ta có thể viết các cơ chế để đặt locker=1 khi có vấn đề với cơ sở dữ liệu để ngắt kết nối nó khỏi ứng dụng.

5. Kết luận

Tôi hy vọng bài viết này đã cung cấp cho bạn một điểm khởi đầu tốt để tìm hiểu về Liquibase. Nếu bạn có bất kỳ câu hỏi nào, đừng ngần ngại hỏi. Cảm ơn bạn đã đọc, và hẹn sớm gặp lại các bạn!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí