Liquibase jest otwartą biblioteką służącą do śledzenia, zarządzania i stosowania zmian w schematach baz danych. Stanowi ono prawdopodobnie najpopularniejsze narzędzie do wersjonowania baz danych w projektach komercyjnych.
Przykład wykorzystania Liquibase
Załóżmy, że mamy dwie instancje tworzonej aplikacji – wersja produkcyjna, oraz wersja developerska. W przypadku uruchomienia aplikacji w wersji developerskiej Liquibase generuje schemat bazy danych oraz dane testowe. W przypadku wersji produkcyjnej pozostaje przy samym schemacie. Takie działanie pozwala na sterowanie działaniami w zależności od uruchomionej instancji. Do realizacji tego zadania potrzebna jest znajomość definiowania profili w Spring.
Najprostsza implementacja
Tworzymy projekt w Spring Boot i dodajemy zależność Liquibase:
<dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> </dependency>
W pierwszej kolejności należy zdefiniować plik konfiguracyjny dla Liquibase. Można może on mieć format XML, JSON, YAML. W tym przykładzie posłużę się XML. Jego szablon powinien wyglądać w tej sposób:
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"> </databaseChangeLog>
W tym momencie nic on nie realizuje, definiuje on tylko przestrzeń nazw do wykorzystania.
Następnie w pliku konfiguracyjnym Spring Boot application.properties podajemy ścieżkę do katalogu w jakim został on utworzony. W moim przypadku jest to:
spring.liquibase.change-log=classpath:/db/changelog/dbchangelog.xml
Teraz można zdefiniować kolejny plik, który odpowiada za tworzenie schematu bazy danych. Dobrą praktyką jest, aby tworzyć plik per modyfikacja. Czyli w tym przypadku pierwszy plik będzie odpowiadał za tworzenie bazy danych, drugi za dodawanie rekordów.
Tworzenie tabeli
Tworze nowy plik o dowolnej nazwie np. addPerson.xml. Następnie wypełniam go następującym kodem:
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.7" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.7 http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.7.xsd"> <changeSet id="1" author="byku"> <createTable tableName="person"> <column name="address" type="varchar(255)"/> </createTable> </changeSet> </databaseChangeLog>
Jest tu zwarta przestrzeń nazw a dodatkowo utworzona struktura tabeli. Znacznik changeSet zawiera informacje o numerze zmiany (później do czego to może się przydać), oraz nazwę autora. Następnie z wykorzystaniem znaczników XML tworzona jest baza danych z jedną kolumną adress.
Składnia jest intuicyjna, lecz wymaga drobnego przyswojenia. Spis całej składni i dostępnych operacji znajdziesz tutaj:
https://www.liquibase.org/documentation/changes/index.html
Teraz wystarczy w głównym pliku konfiguracji Liquibase wskazać namiary do pliku addPerson.xml:
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"> <include file="version/personTable.xml" relativeToChangelogFile="true"/> </databaseChangeLog>
Ja jeszcze wskazałem relativeToChangelogFile, po to, aby mieć możliwość podawania ścieżki relatywnej względem pliku z konfiguracją.
Dodawanie rekordów
Należy stworzyć analogiczny plik addPerson.xml i dodać w nim dodawanie kolumny zgodnie z dokumentacją Liquibase:
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.7" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.7 http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.7.xsd"> <changeSet author="byku" id="2"> <insert schemaName="public" tableName="person"> <column name="address" value="Jana Pawła II" type="varchar(255)"/> </insert> </changeSet> </databaseChangeLog>
Oczywiście następnie trzeba dodać nowy wpis w pliku konfiguracyjnym Liquibase:
<include file="version/addPerson.xml" relativeToChangelogFile="true"/>
Profile
Aby lepiej zobrazować moc Liquibase utworze dwa profile zgodne z treścią przykładu jaki został opisany w nagłówku „Przykład wykorzystania”.
Tworze dwa pliki konfiguracyjne dla Spring Boot (prod i dev):
application-prod.properties spring.datasource.url=jdbc:h2:file:./databaseProd spring.h2.console.enabled=true spring.h2.console.path=/console spring.liquibase.change-log=classpath:/db/changelog/dbchangelog-prod.xml server.port=8082
application-dev.properties spring.datasource.url=jdbc:h2:file:./databaseDev spring.h2.console.enabled=true spring.h2.console.path=/console spring.liquibase.change-log=classpath:/db/changelog/dbchangelog-dev.xml server.port=8081
Różnią się one portami (abym mógł uruchomić obie na jednej maszynie) oraz ścieżkami do pliku konfiguracyjnego Liquibase. Jeden posiada includa tylko do personTable.xml drugi natomiast personTable.xml i addPerson.xml.
Uruchomienie
Teraz zauważymy uruchamiając aplikacje z flagą
-Dspring.profiles.active=prod
lub
-Dspring.profiles.active=dev
Włączona zostanie aplikacja produkcyjna ze sama tabelą albo developerska z dodatkowym rekordem.
Co ląduje w bazie?
Dodatkowe materiały na temat Liquibase
Zachęcam Cię do obejrzenia wido w którym krok po kroku pokazuje implementacje i inne możliwości tej biblioteki 😊
Link do projektu na GitHub:
https://github.com/bykowski/springboot-liquibase