Liquibase ile Veri Tabanı Şema Değişiklik Yönetimi

İçindekiler
- Giriş
- Liquibase Nedir?
- Spring Boot ile Liquibase Entegrasyonu
- Gradle ile Liquibase Kullanımı
- Sık Kullanılan Liquibase Komutları
- Liquibase Kullanırken En İyi Uygulamalar
- Sonuç
- Kaynakça
Giriş
Geliştirilen uygulamaların değişen talep ve ihtiyaçları doğrultusunda, yalnızca uygulama kodunda değil, aynı zamanda veri tabanlarında da çeşitli değişiklikler yapılması gerekmektedir. Özellikle büyük ekiplerde ve çok sayıda ortamda (geliştirme, test, üretim vb.) çalışan projelerde, değişikliklerin doğru ve tutarlı bir şekilde yönetilmesi oldukça zorlu bir süreçtir. Bu zorlukların üstesinden gelmek ve süreci kolaylaştırmak için Liquibase, Flyway ve Alembic gibi araçlar kullanılmaktadır. Bu yazımızda, bu araçlardan Liquibase ele alınacaktır.
Liquibase Nedir?
Liquibase, veri tabanı şemasındaki değişiklikleri yönetmek, izlemek ve bu değişiklikleri farklı ortamlar arasında senkronize etmek için kullanılan açık kaynaklı bir veri tabanı şema değişiklik yönetim çözümüdür. Manuel olarak yapılan veri tabanı değişiklikleri, hataya açık olmasının yanı sıra, hangi değişikliklerin hangi ortamlarda uygulandığını takip etmeyi de zorlaştırır. Liquibase, bu süreci otomatikleştirerek, veri tabanı değişikliklerinin tüm ortamlarda tutarlı ve güvenilir bir şekilde uygulanmasını sağlar.
Ne Sağlıyor?
Veri Tabanı Şemasını Yönetme | Veri tabanı şemasındaki tüm değişiklikleri versiyonlayarak bu değişikliklerin düzenli bir şekilde izlenmesini ve yönetilmesini sağlar. |
Ortamlar Arasında Tutarlılığı Sağlama | Farklı ortamlarda veri tabanı şemasını senkronize eder ve şema değişikliklerinin her ortamda doğru ve tutarlı bir şekilde uygulanmasını sağlar. |
Otomatikleştirilmiş Dağıtım | Veri tabanı değişikliklerini manuel müdahale olmadan otomatik olarak dağıtır, böylece insan hatalarını azaltır. |
Kolay Geri Dönüş (Rollback) | Değişikliklerde sorun çıkması durumunda, veri tabanını önceki kararlı durumuna hızlıca geri döndürmek mümkündür. |
Veri Tabanı Durumunun İzlenmesi | Veri tabanının mevcut durumu merkezi bir yerden takip edilebilir; hangi değişikliklerin uygulandığı ve hangi ortamlarda bulunduğu bilinebilir. |
Çoklu Veri Tabanı Desteği | MySQL, PostgreSQL, Oracle, SQL Server gibi birçok popüler veri tabanı yönetim sistemiyle uyumlu çalışır. |
Geniş Araç Entegrasyonu | Maven, Ant, Gradle, Spring Boot gibi endüstri araçlarıyla ve sürekli entegrasyon (CI), sürekli teslim (CD) süreçleriyle entegre edilebilir. |
Temel Kavramlar
ChangeSet | Veri tabanına uygulanması gereken bir dizi değişikliği temsil eder. Her changeset bir defa çalıştırılır ve Liquibase, değişikliklerin uygulanma durumunu changeset seviyesinde takip eder. |
Change | Veri tabanına uygulanacak tekil bir değişikliği tanımlar. Liquibase, "tablo oluşturma" veya "sütun silme" gibi çeşitli değişiklik türleri sağlar ve her biri SQL komutlarının soyutlamasıdır. |
Changelog | Veri tabanına uygulanacak changeset listesini içeren dosya changelog olarak adlandırılır. Bu dosyalar SQL, YAML, XML veya JSON formatında olabilir. |
Preconditions | Changeset veya changelog'un çalıştırılma kontrolünü sağlar. Yalnızca belirtilen koşullar sağlandığında değişiklikler uygulanır. |
Context | Belirli bir ortamda çalıştırılması gereken changeset’leri sınıflandırmaya yarar. Farklı ortamlarda farklı değişiklikler uygulanabilir. |
Labels | Changeset’leri context’ten daha esnek bir şekilde gruplamak için kullanılır. Çalışma zamanında bir etiket ifadesi geçirilerek, ilgili changeset'ler seçilip çalıştırılabilir. |
Rollback | Veri tabanında yapılan değişikliklerin geri alınmasını sağlar. Genelde bir changeset uygulandıktan sonra hatalarla karşılaşıldığında veya istenilen şema versiyonuna geri dönmek gerektiğinde kullanılır. |
Tag | Veri tabanı şemasının belirli bir anını (veya sürümünü) işaretlemek için kullanılır. Daha sonrasında veri tabanının o anki durumuna kolayca geri dönebilme imkânı sağlar. |
Changelog Parameters | Changelog dosyalarında yer alan yer tutuculardır, çalışma zamanında dinamik olarak değiştirilerek kullanılır. Parametreler sayesinde aynı değişiklik seti, farklı veri tabanları veya ortamlar için uyarlanabilir hale gelir. |
Databasechangelog ve Databasechangeloglock Tabloları |
Liquibase, ilk çalıştırıldığında veri tabanına iki tablo oluşturur: databasechangelog ve databasechangeloglock.
|
Spring Boot ile Liquibase Entegrasyonu
1. Liquibase Bağımlılığını Ekleme
Spring Boot projesine Liquibase desteğini eklemek için öncelikle bağımlılığı eklemek gerekir.
Maven için:
pom.xml
1
2
3
4
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
Gradle için:
build.gradle
1
2
3
dependencies {
implementation 'org.liquibase:liquibase-core'
}
2. Changelog Dosyası Oluşturma
Liquibase, veri tabanı değişikliklerini changelog dosyalarında saklar. Bu dosyalar, genellikle src/main/resources/db/changelog/
dizini altında saklanır.

Yönetimin kolay olması adına ana bir changelog dosyası (genellikle db.changelog-master.xml
olarak adlandırılır) oluşturulup diğer tüm alt changelog dosyaları buraya dahil edilebilir. Bu, projede düzen ve takip açısından kolaylık sağlar.
db.changelog-master.xml
1
2
3
4
5
6
7
8
9
10
11
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
<!-- Diğer changelog dosyaları -->
<include file="db/changelog/data/kullanici.xml"/>
<include file="db/changelog/data/siparis.xml"/>
</databaseChangeLog>
Alt changelog dosyaları belirli alanlara yönelik değişiklikleri içerir. Örneğin, kullanici.xml
dosyası sadece kullanıcı ile ilgili veri tabanı değişikliklerini içerir.
kullanici.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
<changeSet id="1" author="deren-toy">
<createTable tableName="kullanici">
<column name="id" type="int">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="isim" type="varchar(255)"/>
<column name="email" type="varchar(255)"/>
</createTable>
</changeSet>
</databaseChangeLog>
3. application.properties
Dosyasını Yapılandırma
Liquibase’in, hangi changelog dosyasını kullanacağını belirtmek için application.properties
dosyasını aşağıdaki şekilde yapılandırmak gerekir:
1
spring.liquibase.change-log=/db/changelog/db.changelog-master.xml
Bu yapılandırmalardan sonra Spring Boot uygulaması başlatıldığında, Liquibase otomatik olarak db.changelog-master.xml
dosyasında tanımlı olan değişiklikleri veri tabanına uygular. Changelog dosyaları databasechangelog
tablosundan görüntülenebilir.

Eğer Liquibase’e ait komutları Spring Boot uygulamasının ayağa kalkmasından bağımsız bir şekilde çalıştırmak gerekiyorsa, manuel komutlar için Gradle ya da Maven eklentisi kullanılabilir. Bu sayede veri tabanı değişiklikleri, uygulama başlatılmadan doğrudan komut satırından kontrol edilebilir. Gradle ile Liquibase komutlarını çalıştırmak, örneğin veri tabanı güncellemeleri, geri alma veya şema doğrulama gibi işlemleri uygulamadan bağımsız olarak yönetmeyi sağlar ve CI/CD süreçlerinde otomasyonu kolaylaştırır.
Gradle ile Liquibase Kullanımı
Gradle ile Liquibase komutlarını çalıştırmak için aşağıdaki adımlar takip edilmelidir.
1. Liquibase Gradle Eklentisinin Eklenmesi:
build.gradle
dosyasına aşağıdaki eklenti eklenir:
1
2
3
plugins {
id 'org.liquibase.gradle' version '2.2.0'
}
2. Liquibase Çalışma Zamanı (Runtime) Bağımlılıklarının Eklenmesi:
Gradle üzerinden manuel Liquibase komutları (update
, rollback
, vb.) çalıştırmak için Liquibase core, ilgili JDBC sürücüsü ve picocli gibi bileşenlerin liquibaseRuntime
olarak build.gradle
‘a eklenmesi gerekir:
1
2
3
4
5
dependencies {
liquibaseRuntime 'org.liquibase:liquibase-core:4.27.0'
liquibaseRuntime 'org.postgresql:postgresql:42.7.3'
liquibaseRuntime 'info.picocli:picocli:4.7.5'
}
3. Liquibase Yapılandırılmasının Eklenmesi:
Gradle eklentisi Liquibase’i çalıştırabilmek için hangi veri tabanına bağlanacağını ve hangi changelog dosyasını kullanacağını bilmek zorundadır. Bu nedenle, build.gradle
dosyasına aşağıdaki bilgiler eklenmelidir:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
liquibase {
activities {
main {
// Changelog dosyasının yolu
changelogFile '/db/changelog/db.changelog-master.xml'
// Changelog dosyalarının aranacağı dizin
searchPath 'src/main/resources'
// Veri tabanı bağlantı bilgileri
url 'jdbc:postgresql://localhost:5432/mydb'
username 'deren'
password 'deren'
driver 'org.postgresql.Driver'
}
}
// Çalıştırılacak aktivite
runList = 'main'
}
Bu aşamadan sonra, Liquibase komutları Gradle ile çalıştırılabilir hale gelmektedir.
Sık Kullanılan Liquibase Komutları
1. Update:
databasechangelog
tablosunu kontrol eder ve henüz çalıştırılmamış changeset’leri veri tabanına uygular.
1
./gradlew update
2. Rollback:
Uygulanan changeset’leri geri almak için kullanılır. Belirtilen tarih, etiket veya changeset sayısına göre veri tabanı değişikliklerini geri alabilme imkânı sunar.
- Tarihe göre değişiklikleri geri almak için:
1
./gradlew rollback -PliquibaseCommandValue=YYYY-MM-DD
- Etikete göre değişiklikleri geri almak için:
1
./gradlew rollback -PliquibaseCommandValue=etiketIsmi
- Son uygulanan değişiklikleri geri almak için:
1
./gradlew rollbackCount -PliquibaseCommandValue=1
Bu komut, son uygulanan bir changeset’i geri alır. 1 yerine istenilen sayı yazılarak daha fazla changeset geri alınabilir.
3. GenerateChangeLog:
Veri tabanının mevcut durumunu Liquibase changelog formatına çevirmek için kullanılır. Daha önce Liquibase kullanılmayan bir projeden Liquibase’e geçiş yapılacağı durumda kullanışlı olabilir.
1
./gradlew generateChangelog
4. Status:
Veri tabanına mevcut tüm changeset’lerin uygulanıp uygulanmadığını, yani veri tabanının güncel olup olmadığını kontrol eder. Komutu çalıştırdıktan sonra henüz uygulanmamış changeset’leri görmek mümkündür.
1
./gradlew status
5. Validate:
Changelog dosyalarında hata olup olmadığını kontrol eder ve yanlış yapılandırılmış changeset’leri bulur. Hataları önceden tespit etmek ve Liquibase changelog dosyasını dağıtmadan önce düzeltmek için kullanışlıdır.
1
./gradlew validate
Liquibase Kullanırken En İyi Uygulamalar
1. Changeset’leri Benzersiz Tanımlamak:
Liquibase changeset’lerin benzersizlik kontrolünü ID
, author
ve changelog dosyasının tam yolu
ile yapar, yani farklı dosyalarda aynı ID
ve author
ile changeset tanımlanırsa herhangi bir hata alınmaz. Fakat yine de changeset id’nin her zaman benzersiz olmasını sağlamak iyi bir yaklaşımdır. Id’nin nasıl oluşturulacağının belirli bir şablona oturtulması, geliştirme ekipleri için kolaylıklar sağlar. Örneğin, yıl-ay-gün, yazar ismi ve saat birleştirilerek benzersiz changeset id’ler yaratılarak kullanılabilir.
1
<changeSet id="20240914 deren.toy:1427"></changeSet>
2. Her Changeset’in Geri Alınabilir Olmasını Sağlamak:
Changeset’lere rollback
komutlarını eklemek, hata çıktığı durumda veri tabanını önceki haline döndürmeyi kolaylaştırır. Liquibase’in otomatik olarak geri alamayacağı veya geri alma işlemini doğru bir şekilde yapamayacağı, örneğin eklenen bir verinin rollback
komutu çalıştığında silinmesi, veri tipinin ya da sütun isminin değiştirilmesi gibi, karmaşık değişikliklerde kullanılması gerekmektedir.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<changeSet id="1" author="deren-toy">
<!-- Yeni veri ekleme -->
<insert tableName="kullanici">
<column name="id" value="1"/>
<column name="isim" value="Deren Toy"/>
<column name="email" value="derentoy@gmail.com"/>
</insert>
<!-- Rollback için manuel veri silme tanımı -->
<rollback>
<delete tableName="kullanici">
<where>id = 1</where>
</delete>
</rollback>
</changeSet>
3. Changelog Dosyalarını Küçük Tutmak ve Ana Bir Changelog Dosyası Oluşturmak:
Değişiklikleri büyük tek bir changelog dosyası yerine, küçük ve modüler dosyalar halinde tutmak, yönetimi ve izlemeyi kolaylaştırır. “Changelog Dosyası Oluşturma” kısmında anlatıldığı gibi, ana bir changelog dosyası oluşturulup diğer alt changelog dosyaları buradan yönetilebilir. Aynı zamanda, her changeset içinde tek bir değişiklik yapmak, değişikliklerin geri alınabilmesini kolaylaştırır.
4. Sırayı Korumak:
Ana changelog dosyasına eklenen diğer changelog dosyaları sıralı bir şekilde çalıştırılır. Bu nedenle, en yeni değişiklik en altta olacak şekilde eklenmelidir. Aksi durumda, istenmeyen senaryolar ortaya çıkabilir.
1
2
3
4
5
6
7
8
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
<include file="db/changelog/data/kullanici.xml"/> <!-- siparis.xml'den önce çalışacaktır -->
<include file="db/changelog/data/siparis.xml"/>
</databaseChangeLog>
5. Preconditions Kullanmak:
Changeset’ler çalışmadan önce veri tabanının istenilen durumda olup olmadığı preconditions
ile kontrol edilebilir. Bu sayede, değişiklikler daha güvenli bir şekilde uygulanır. Örneğin, kullanıcı bilgilerini tutan ayrı bir tablo yaratmak isteniyorsa; öncesinde kullanıcı tablosu var mı diye kontrol etmek için preconditions
kullanılabilir. Kullanıcı tablosu mevcut değil ise aşağıdaki changeset çalışmayacak, çalışmış olarak işaretlenecektir.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
<changeSet id="5" author="deren-toy">
<preConditions onFail="MARK_RAN">
<tableExists tableName="kullanici"/>
</preConditions>
<createTable tableName="kullanici_bilgisi">
<column name="id" type="int">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="kullanici_id" type="int">
<constraints nullable="false" foreignKeyName="fk_kullanici_bilgisi_kullanici"
references="kullanici(id)"/>
</column>
<column name="telefon" type="varchar(15)"/>
<column name="adres" type="varchar(255)"/>
</createTable>
</changeSet>
</databaseChangeLog>
6. Daha Önce Çalıştırılmış Changeset’leri Güncellememek:
Daha önceden yazılmış ve çalıştırılmış olan changeset’ler üzerinde değişiklik yapmaktan kaçınmak gerekir. Eklenecek olan yeni değişiklik, yeni bir changeset’te yazılmalıdır. Liquibase, çalıştırdığı dosyaların kaydını tutar ve eski bir changeset değiştirildiğinde doğrulama hatası alınır. Örneğin, kullanıcı tablosundaki email
sütununun ismi değiştirilmek ve bu tabloya yeni bir sütun eklenmek istenirse; kullanıcı.xml
dosyasındaki changeset değiştirilmemelidir ve yeni bir changeset yazılmalıdır:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.5.xsd">
<changeSet id="2" author="deren-toy">
<!-- email sütununu email_adresi olarak yeniden adlandır -->
<renameColumn tableName="kullanici" oldColumnName="email" newColumnName="email_adresi" columnDataType="varchar(255)"/>
</changeSet>
<changeSet id="3" author="deren-toy">
<!-- soyAd sütunu ekle -->
<addColumn tableName="kullanici">
<column name="soyAd" type="varchar(255)"/>
</addColumn>
</changeSet>
</databaseChangeLog>
7. Değişiklikleri Uygulamadan Önce Kontrol Etmek:
Yapılan değişiklikleri üretim ortamına uygulamadan önce test/demo gibi bir ortamda uygulamak oluşabilecek hataların önden yakalanmasını sağlar. Örneğin, herhangi bir tabloya not null
bir sütun eklenecekse ve mevcuttaki verilerde bu alanın ne olacağı changeset dosyasına dahil edilmediyse; çalışan changeset sonrasında hata alınır. Bunun gibi senaryoların yaşanmaması için değişikliklerin uygulanmadan önce test edilmesi önemlidir.
Sonuç
Liquibase, büyük ve karmaşık projelerde veri tabanı değişikliklerinin yönetimini kolaylaştırarak, güvenilir geri alma mekanizmaları ve şema yönetimi gibi özellikleriyle öne çıkmaktadır. Özellikle Spring Boot ve Gradle ile entegrasyonu sayesinde veri tabanı süreçlerini otomatikleştirmektedir.
Bu yazıda, Liquibase’in sunduğu avantajlar, temel kavramlar, Spring Boot ile entegrasyonu, Gradle ile kullanımı, sık kullanılan komutlar ve en iyi uygulamalar ele alınmıştır. Projelerin ihtiyaçlarına uygun veri tabanı şema değişikliği yönetim aracı seçilirken, Liquibase’in güçlü bir seçenek olduğu unutulmamalıdır.
Kaynakça
- https://docs.liquibase.com/home.html
- https://reflectoring.io/database-migration-spring-boot-liquibase/
Yazımızın teknik gözden geçirmesi için İmtisal Akdede’ye, editör desteği için ise Kübra Ertürk’e teşekkür ederiz.