Liquibase

Mariusz Szulist
November 4, 2015 | Software development

Zazwyczaj skrypty aktualizujące bazę są dostarczane w plikach .sql z README informującym o kolejności wykonywania skryptów, bazie danych na jakiej przeprowadzono testy itp. Problem zaczyna się w momencie gdy z jakiejś przyczyny skrypt zawierający procedury zmieniające strukturę bazy zatrzyma się w połowie z przyczyn losowych.
Stoimy wtedy przed problemem, które kwerendy się wykonały (jak wiadomo polecenia DDL są od razu commitowane, a bazy SQL nie wspierają transakcyjności w tej kwestii). W tym i innych problemach pomoże nam liquibase.

Co to jest liquibase?

W telegraficznym skrócie: liquibase to niezależna od bazy danych biblioteka do zarządzania zmianami na niej. Dla swoich celów zarządczych liquibase tworzy w bazie danych dwie tabele:

CREATE TABLE databasechangelog
(
  id character varying(255) NOT NULL,
  author character varying(255) NOT NULL,
  filename character varying(255) NOT NULL,
  dateexecuted timestamp without time zone NOT NULL,
  orderexecuted integer NOT NULL,
  exectype character varying(10) NOT NULL,
  md5sum character varying(35),
  description character varying(255),
  comments character varying(255),
  tag character varying(255),
  liquibase character varying(20),
  contexts character varying(255),
  labels character varying(255)
);

CREATE TABLE databasechangeloglock
(
  id integer NOT NULL,
  locked boolean NOT NULL,
  lockgranted timestamp without time zone,
  lockedby character varying(255),
  CONSTRAINT pk_databasechangeloglock PRIMARY KEY (id)
);

Zarządzanie zmianą

Wszelkie zmiany na bazie danych są zapisywane w pliku changelog. Dostępne formaty, w których można stworzyć plik zarządzania zmianą to:

  • XML
  • YAML
  • JSON
  • SQL
  • Inne wspierane na zasadzie community

Plik changelog składa się z wielu małych powiązanych logicznie i transakcyjnie zbiorów. Każdy taki zbiór zmian znajduje się w osobnym, którego unikalność zapewniają atrybuty „id” oraz „author”. Liquibase uruchamia każdy z changeSet w osobnej transakcji, która w razie błędu jest wycofywana za pomocą zdefiniowanego przez programistę rollback’u.

Zamiast id można wykorzystywać ciąg znaków odpowiadający dacie utworzenia zmiany, dzięki czemu dodajemy sobie informację w jakiej kolejności poukładać changeSet w razie konfliktu na repozytorium kodu.

Zmiana myślenia o zmianie bazy

W projektach, w których uczestniczę liquibase jest zainstalowany i zintegrowany z narzędziami budowania aplikacji. W moim przypadku jest to maven. Za każdym razem kiedy widzę, że są zmiany w pliku changelog wykonuję skrypt liquibase, tym samym od razu testuję zmiany bazy napisane przez innych programistów, a następnie wdrażam aplikację. Takie podejście uwalnia nas jako zespół od stresu testowania skryptów bazodanowych całościowo tuż przed wdrożeniem, naturalnie robimy to iteracyjnie.

Dzięki temu, że liquibase zakłada swoje tabele informujące go przy wdrożeniu o wprowadzonych wcześniej zmianach, wykonuje tylko te które są niezbędne. Jest to dodatkowa zaleta, gdyż zwalnia nas to po części z pamiętania, do którego miejsca nasz skrypt aktualizacyjny został wykonany na produkcji, co wdrożyć, co było wdrożone itp. Dostajemy to za darmo! Można to porównać do wersjonowania kodu (SVN, git, etc).

Uruchamianie liquibase i wspierane bazy

Uruchamiać liquibase możemy za pomocą:

  • Linii poleceń
  • Ant
  • Maven
  • Spring
  • Servlet Listener
  • CDI

Bazy danych, z którymi możemy pracować korzystając z tej biblioteki to:

  • MySQL
  • PostgreSQL
  • Oracle
  • SQL Server
  • Sybase_Enterprise
  • Sybase_Anywhere
  • DB2
  • Apache_Derby
  • HSQL
  • H2
  • Informix
  • Firebird
  • SQLite

Osobiście potwierdzam bezproblemowe działanie na PostgreSQL oraz SQL Server (więcej).
Jak już pisałem ja osobiście do developerki używam narzędzia maven. Natomiast dla klienta wysyłamy archiwum ZIP, który zawiera:

  • Jar’a z biblioteką jdbc dla bazy
  • Plik changelog
  • Jar’a z liquibase i skrypt „update”, który wygląda podobnie do kodu poniżej:
    
    #! /bin/sh
     
    SERVER=$1
    PORT=$2
    DATABASE=$3
    USER=$4
    PASSWORD=$5
    java    -jar liquibase.jar 
                    --driver=com.microsoft.sqlserver.jdbc.SQLServerDriver 
                    --classpath=lib/sqljdbc4-4.0.jar 
                    --changeLogFile=conf/db-changelog.xml 
                    --"url=jdbc:sqlserver://${SERVER}:${PORT};DatabaseName=${DATABASE}" 
                    --username=${USER} 
                    --password=${PASSWORD} 
                    update
    
    
  • Oraz README z przykładową treścią:
    sh update    localhost      1433    baza   user   haslo123
    

W razie katastrofy zawsze można wszystko przywrócić.
To tylko zajawka i nie pokrywa całej funkcjonalności. 

Na koniec odsyłam do oficjalnej dokumentacji i zachęcam do dalszej lektury.

Każdy zespół projektowy po oddaniu projektu staje przed problemem aktualizacji aplikacji, która funkcjonuje produkcyjnie. Wiadomo, że z czasem dochodzą nowe wymagania, programiści dopisują nowe funkcjonalności, które często mają bezpośredni wpływ na model bazodanowy.
W klasycznym przypadku przy wdrożeniu nowej aplikacji na środowisko Klienta programiści muszą dostarczyć nową aplikację oraz skrypty aktualizujące bazę danych. W tym artykule będę chciał przedstawić narzędzie wspomagające aktualizację bazy danych.

Chcesz poznać nas lepiej? Dowiedz się, co nas wyróżnia.