Spring Boot එක්ක Liquibase | DataBases Manage කරමු SC Guide

Spring Boot එක්ක Liquibase | DataBases Manage කරමු SC Guide

ආයුබෝවන් කට්ටියට! අද අපි කතා කරන්න යන්නේ ඔයාලා ගොඩක් දෙනෙක්ට තියෙන කරදරකාරී ප්‍රශ්නයකට ලස්සන විසඳුමක් ගැන. ඒ තමයි ඩේටාබේස් වෙනස්කම් මැනේජ් කරන එක. කණ්ඩායමක් විදියට වැඩ කරද්දි, Dev Environment එකෙන් QA එකට, එතනින් Production එකට යද්දි ඩේටාබේස් schema එක මැනේජ් කරන එක හරිම අමාරු වැඩක් නේද? එකේදි බඩු අල වෙන්න තියෙන ඉඩත් වැඩියි. 'අනේ මගේ මැෂින් එකේ වැඩ කරානේ!' කියන කතාව හැමෝටම අහන්න සිද්ධ වෙලා ඇති. මේකට තමයි Liquibase කියන සුපිරි ටූල් එක අපිට උදව් වෙන්නේ. Spring Boot එක්ක Liquibase කොහොමද හරියටම පාවිච්චි කරන්නේ කියලා අද අපි මුල ඉඳන්ම පියවරෙන් පියවර බලමු. මේක ඔයාලගේ Project වල Quality එකට වගේම, Release Process එක වේගවත් කරන්නත් ගොඩක් උදව් වෙයි. එහෙනම් වැඩේට බහිමු!

අපිට සාමාන්‍යයෙන් ඩේටාබේස් වල වෙනස්කම් කරන්න වෙන වෙලාවල් ඕනෑ තරම් එනවනේ. අලුත් Table එකක් හදන්න, තියෙන Table එකකට Column එකක් එකතු කරන්න, Data Type එකක් වෙනස් කරන්න, Index එකක් දාන්න වගේ දේවල්. මේ හැම වෙනස්කමක්ම හරියට Track කරගෙන, හැම Developer කෙනෙක්ගේම ඩේටාබේස් එක Synchronize කරගෙන, Production එකට Deploy කරද්දි කිසිම අවුලක් නැතුව වැඩේ ගොඩ දාගන්න එක තමයි අභියෝගය. අන්න ඒකට තමයි Liquibase වගේ Database Version Control System එකක් අත්‍යවශ්‍ය වෙන්නේ. මේක හරියට Software Code Version Control කරනවා වගේම Database Schema එකත් Version Control කරනවා වගේ වැඩක්.

මොකක්ද මේ Liquibase?

සරලව කිව්වොත්, Liquibase කියන්නේ Open-source Library එකක්. මේක අපිට පුළුවන් Database Schema Changes Track කරන්න, Manage කරන්න, වගේම Apply කරන්නත් පාවිච්චි කරන්න. මේක Database Independent නිසා MySQL, PostgreSQL, Oracle, SQL Server වගේ ඕනෑම ඩේටාබේස් එකක් එක්ක වැඩ කරන්න පුළුවන්. මේකෙන් වෙන්නේ මොකක්ද? අපි කරන හැම ඩේටාබේස් වෙනස්කමක්ම (ඒ කියන්නේ Schema Changes) Source Control එකේ තියාගෙන, ඒ වෙනස්කම් පිළිවෙළකට ඕනෑම ඩේටාබේස් එකකට Apply කරන්න පුළුවන් වෙන එක. ඒ වගේම ඕනෑ වෙලාවක අපි කරපු Changes Rollback කරන්නත් පුළුවන්.

අපි හිතමු ඔයාලා Developersලා පස් දෙනෙක් ඉන්න Team එකක් කියලා. එක Developer කෙනෙක් Table එකක් හදනවා, තව කෙනෙක් ඒකට Column එකක් එකතු කරනවා. මේ හැමෝම තමන්ගේ Local Machine වල මේ Changes කරලා අන්තිමට ඒ හැම Changes එකක්ම එක ඩේටාබේස් එකකට ගේන එක අමාරුයිනේ. Liquibase එකෙන් මේ ප්‍රශ්නය විසඳනවා. මේකෙදි අපේ Database වෙනස්කම් YAML, XML, JSON, නැත්නම් Straight SQL කියන Formats වලින් ලිව්වට පස්සේ, Liquibase එකෙන් ඒ Changes ටික Database එකට Apply කරනවා. ඒ වගේම Liquibase එකෙන් අපේ ඩේටාබේස් එක ඇතුලේ Table එකක් හදනවා DATABASECHANGELOG කියලා. අපි Apply කරන හැම Change එකක්ම ඒ Table එකේ Record වෙනවා. ඒ නිසා මොන Changes ටිකද Apply කරලා තියෙන්නේ, මොනවද නැත්තේ කියලා දැනගන්න පුළුවන්.

Liquibase වලින් ලැබෙන ප්‍රධාන ලාභ

  • Version Control: අපි කරන හැම Database Schema Changes එකක්ම Version Control එකක තියෙනවා වගේ Manage කරන්න පුළුවන්.
  • Environment Consistency: Dev, QA, Production වගේ හැම Environment එකකම Database Schema එක එක වගේ තියාගන්න පුළුවන්. 'මගේ මැෂින් එකේ වැඩ කරනවා' කියන ප්‍රශ්නයට තිත තියන්න පුළුවන්.
  • Rollbacks: වැරදීමක් වුනොත් කලින් තිබ්බ තත්වෙට Database එක Rollback කරන්න පුළුවන්.
  • Team Collaboration: Developersලා කීප දෙනෙක් එකට වැඩ කරද්දි Conflict අඩු කරගෙන, Changes Merge කරගන්න පහසුවෙනවා.
  • CI/CD Integration: Continuous Integration / Continuous Deployment (CI/CD) Pipeline එකට පහසුවෙන් Integrate කරන්න පුළුවන්. Build එකක් Run වෙද්දිම Database Migrations ටිකත් Automatic ව Apply කරන්න පුළුවන්.

Spring Boot එක්ක Liquibase සෙට් කරගමු!

Spring Boot Project එකකට Liquibase එකතු කරන එක හරිම ලේසියි. මුලින්ම අපේ Project එකට Liquibase Dependency එක එකතු කරගන්න ඕනේ.

1. Dependency එක එකතු කිරීම

ඔයාලා Maven පාවිච්චි කරනවා නම් pom.xml එකට මේ Dependency එක එකතු කරන්න:

<!-- pom.xml -->
<dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
</dependency>

Gradle පාවිච්චි කරනවා නම් build.gradle එකට මේ විදියට එකතු කරන්න:

// build.gradle
implementation 'org.liquibase:liquibase-core'

මේ Dependency එක දැම්මාම Spring Boot එක Auto-configure වෙනවා Liquibase එක්ක වැඩ කරන්න. ඒ කියන්නේ අපිට වෙනම Extra Configurations කරන්න ඕනේ නැහැ.

2. Configuration කිරීම

ඊළඟට අපේ application.properties (හෝ application.yml) file එකේ Liquibase Configurations ටික සෙට් කරගන්න ඕනේ. සාමාන්‍යයෙන් අපේ Database Configurations එක්කම මේක කරන්න පුළුවන්.

# application.properties

# Database Connection Properties (මේ ටික සාමාන්‍යයෙන් දාන ඒවා)
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=none # මේක none කරන එක වැදගත්! Liquibase නිසා Schema එක හදන එක hibernate වලට භාර දෙන්නේ නැහැ.

# Liquibase Configuration Properties
spring.liquibase.enabled=true # Liquibase Enable කරනවා
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yaml # අපේ Master Changelog file එක තියෙන තැන. මේ Path එක වැදගත්.

මතක තියාගන්න, spring.jpa.hibernate.ddl-auto=none කියලා දාන එක ගොඩක් වැදගත්. මොකද නැත්නම් Hibernate එකෙන් අපේ Entity Classes වලින් Database Schema එක හදන්න හදනවා. ඒක අපිට ඕනේ නැහැ Liquibase පාවිච්චි කරනවා නම්. Liquibase තමයි Database Schema එක හදාගෙන යන්නේ.

3. Master Changelog File එක හදමු

දැන් අපේ Project එකේ src/main/resources ෆෝල්ඩර් එක ඇතුලේ db/changelog කියලා ෆෝල්ඩර් එකක් හදලා ඒක ඇතුලේ db.changelog-master.yaml (නැත්නම් .xml හෝ .json) කියලා Master Changelog File එක හදන්න ඕනේ. මේ File එක තමයි අපි කරන හැම Database Change එකක්ම (හෝ වෙනම Files වල තියෙන Changesets) එකට එකතු කරන ප්‍රධාන File එක.

# src/main/resources/db/changelog/db.changelog-master.yaml

databaseChangeLog:
  - include:
      file: db/changelog/changes/001-create-users-table.yaml
  - include:
      file: db/changelog/changes/002-add-address-column.yaml
  - include:
      file: db/changelog/changes/003-add-not-null-constraint.yaml
  - include:
      file: db/changelog/changes/004-insert-initial-users.yaml

මෙහිදී include කියන Tag එකෙන් අපිට පුළුවන් වෙනම File වල තියෙන Changesets මේ Master File එකට එකතු කරන්න. මේක ගොඩක් වැදගත්, මොකද හැම Changeset එකක්ම එකම Master File එකේ දැම්මොත් ඒක Manage කරන එක අමාරු වෙනවා. ඒ නිසා අපි හැම ලොකු Change එකකටම වෙනම File එකක් හදලා ඒක Master File එකට Include කරනවා.

Changelogs සහ Changesets: වැඩේ කරන හැටි!

Liquibase වලදී අපිට තියෙන ප්‍රධානම සංකල්ප දෙක තමයි Changelog සහ Changeset කියන්නේ. මේවා හරියට තේරුම් ගත්තොත් වැඩේ ලේසියි.

Changelog මොකක්ද?

Changelog කියන්නේ Database Migrations වල Master File එක. අපි කලින් හැදුව db.changelog-master.yaml File එක ඒ වගේ එකක්. මේ File එකේ තමයි අපි කරන හැම Database Change එකක්ම (Changeset) තියෙන්නේ, නැත්නම් ඒ Changesets තියෙන වෙනත් Files වලට Reference කරලා තියෙන්නේ. Liquibase එකෙන් මේ Changelog File එක කියවලා, ඒකේ තියෙන Changesets ටික පිළිවෙළට Database එකට Apply කරනවා. ඒ වගේම Liquibase එකෙන් අපේ Database එකේ DATABASECHANGELOG කියලා Table එකක් හදනවා. මේ Table එකේ අපි Apply කරපු හැම Changeset එකක්ම Record වෙනවා. ඒ නිසා Liquibase එකට හරියටම දන්නවා මොන Changes ටිකද Apply කරලා තියෙන්නේ, මොනවද නැත්තේ කියලා. ඒ නිසා එකම Changeset එක දෙපාරක් Run වෙන්නේ නැහැ. මේක තමයි Idempotency කියන්නේ.

Changeset මොකක්ද?

Changeset එකක් කියන්නේ එක Database Change එකක්. ඒක තමයි Database වලට යන Actual Change එක. උදාහරණයක් විදියට, Table එකක් හදන එක, Column එකක් එකතු කරන එක, Index එකක් හදන එක වගේ. හැම Changeset එකකටම id එකක් සහ author කෙනෙක් අනිවාර්යයෙන්ම තියෙන්න ඕනේ. මේ id එකයි, author කෙනයි එකතු වෙලා තමයි Changeset එකක Unique Identity එක හැදෙන්නේ. ඒ නිසා මේ දෙකම හැම Changeset එකකටම Unique වෙන්න ඕනේ. උදාහරණයක් විදියට id: 001-create-users-table-v1 සහ author: isuru වගේ දේවල් දාන්න පුළුවන්. id එකට Timestamp එකක් දාන එකත් හොඳ පුරුද්දක්.

වැදගත්: Changeset එකක් Database එකට Apply කලාට පස්සේ ඒක වෙනස් කරන්න (Modify) කරන්න හොඳ නැහැ. එහෙම කලොත් Liquibase එකට ඒ Changeset එකේ Checksum එක Compare කරද්දි අවුලක් වෙනවා. ඒ නිසා Changeset එකක් modify කරන්න ඕනේ නම්, අලුත් Changeset එකක් හදලා කලින් Changeset එකේ Change එක නිවැරදි කරන්න.

Changeset එකක් ඇතුලේ අපිට විවිධ Database Operations කරන්න පුළුවන්. ඒවාට අපි කියනවා Change Types කියලා. සමහර පොදු Change Types ටිකක් පහලින් බලන්න:

  • createTable: අලුත් Table එකක් හදන්න.
  • addColumn: Table එකකට අලුත් Column එකක් එකතු කරන්න.
  • addNotNullConstraint: Column එකකට NOT NULL Constraint එකක් එකතු කරන්න.
  • dropColumn: Column එකක් අයින් කරන්න.
  • renameColumn: Column එකක නම වෙනස් කරන්න.
  • sql: Custom SQL Query එකක් Run කරන්න.
  • sqlFile: SQL File එකක තියෙන Queries ටික Run කරන්න.
  • insert: Table එකකට Data Insert කරන්න.
  • loadData: CSV File එකකින් Data Insert කරන්න.

ප්‍රැක්ටිකල් උදාහරණයක්: යූසර්ලාගේ ඩේටාබේස් මැනේජ් කරමු!

දැන් අපි පොඩි Practical Example එකක් කරමු. අපි සරල User Management System එකක් හදනවා කියලා හිතමු. ඒකට අවශ්‍ය Database Changes ටික Liquibase පාවිච්චි කරලා කොහොමද කරන්නේ කියලා බලමු.

1. Users Table එක හදමු

මුලින්ම අපි users කියලා Table එකක් හදමු. මේකට id, username, password වගේ Columns තියෙයි. මේ Changeset එක db/changelog/changes/001-create-users-table.yaml කියන File එකේ හදමු.

# src/main/resources/db/changelog/changes/001-create-users-table.yaml

databaseChangeLog:
  - changeSet:
      id: 001-create-users-table
      author: isuru
      changes:
        - createTable:
            tableName: users
            columns:
              - column:
                  name: id
                  type: BIGINT
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: username
                  type: VARCHAR(255)
                  constraints:
                    unique: true
                    nullable: false
              - column:
                  name: password
                  type: VARCHAR(255)
                  constraints:
                    nullable: false
              - column:
                  name: created_at
                  type: TIMESTAMP
                  defaultValueComputed: CURRENT_TIMESTAMP
                  constraints:
                    nullable: false

මේ File එක Master Changelog එකට include කරන්න අමතක කරන්න එපා. (අපි කලින් Master File එකේ දාලා තියෙන්නේ).

2. Address Column එක එකතු කරමු

දැන් අපේ Users ලට Address එකක් Store කරන්න ඕනේ කියලා හිතමු. ඒකට අපි users Table එකට address කියන Column එක එකතු කරමු. මේක වෙනම Changeset එකක් විදියට db/changelog/changes/002-add-address-column.yaml කියන File එකේ හදමු.

# src/main/resources/db/changelog/changes/002-add-address-column.yaml

databaseChangeLog:
  - changeSet:
      id: 002-add-address-column
      author: isuru
      changes:
        - addColumn:
            tableName: users
            columns:
              - column:
                  name: address
                  type: VARCHAR(255)
                  constraints:
                    nullable: true # මුලින් nullable: true විදියට දාමු

මේකත් Master Changelog එකට include කරන්න.

3. Address Column එකට NOT NULL Constraint එකක් දාමු

පස්සේ අපිට හිතුනා Users ලගේ Address එක අනිවාර්යයෙන්ම තියෙන්න ඕනේ කියලා. ඒ නිසා අපි address Column එකට NOT NULL Constraint එකක් දාමු. මේකත් වෙනම Changeset එකක් විදියට db/changelog/changes/003-add-not-null-constraint.yaml කියන File එකේ හදමු.

# src/main/resources/db/changelog/changes/003-add-not-null-constraint.yaml

databaseChangeLog:
  - changeSet:
      id: 003-add-not-null-constraint
      author: isuru
      changes:
        - addNotNullConstraint:
            tableName: users
            columnName: address
            columnDataType: VARCHAR(255)
            # precondition එකක් දාමු මේක Run වෙන්න කලින් address column එකේ null values තියෙනවද බලන්න
            # if there are existing null values, the migration will fail. So we might need to update them first.
            # For simplicity, we are skipping the update here.

මේකත් Master Changelog එකට include කරන්න. මෙතනදී, addNotNullConstraint එක දාන්න කලින් දැනට address Column එකේ NULL Values තියෙනවා නම්, ඒවාට Default Value එකක් දාන්න හෝ Update කරන්න ඕනේ. නැත්නම් Migration එක Fail වෙන්න පුළුවන්. ඒකට වෙනම Changeset එකක් හදන්න පුළුවන් sql Change Type එක පාවිච්චි කරලා.

4. Initial Data Insert කරමු

අවසාන වශයෙන්, අපි Project එක ආරම්භ කරද්දි Database එකට අවශ්‍ය Initial User Data ටිකක් Insert කරමු. මේක db/changelog/changes/004-insert-initial-users.yaml කියන File එකේ හදමු.

# src/main/resources/db/changelog/changes/004-insert-initial-users.yaml

databaseChangeLog:
  - changeSet:
      id: 004-insert-initial-users
      author: isuru
      changes:
        - insert:
            tableName: users
            columns:
              - column: {name: username, value: "john.doe"}
              - column: {name: password, value: "hashedpassword123"}
              - column: {name: address, value: "123 Main St, Colombo"}
        - insert:
            tableName: users
            columns:
              - column: {name: username, value: "jane.smith"}
              - column: {name: password, value: "hashedpassword456"}
              - column: {name: address, value: "456 Elm St, Kandy"}

මේකත් Master Changelog එකට include කරන්න.

දැන් ඔයාලගේ Spring Boot Application එක Run කරද්දි, Liquibase එකෙන් මේ හැම Changeset එකක්ම පිළිවෙළට Database එකට Apply කරනවා. ඔයාලට පුළුවන් ඩේටාබේස් එකේ DATABASECHANGELOG Table එක බලලා මොන Changesets ටිකද Run වෙලා තියෙන්නේ කියලා Check කරන්න. මේක ඇතුලේ ID, Author, FILENAME, DATEEXECUTED, MD5SUM (Checksum) වගේ විස්තර තියෙනවා.

හොඳම පුරුදු සහ ටිප්ස් (Machang!)

Liquibase පාවිච්චි කරද්දි අපි අනුගමනය කරන්න ඕනේ හොඳම පුරුදු ටිකක් තියෙනවා. මේවා අනුගමනය කලොත් වැඩේ ගොඩ දාගන්න පුළුවන්.

  • එක Changeset එකකට එක Logical Change එකක්: හැම Changeset එකක්ම එක Logical Change එකක් විතරක් කරන්න ඕනේ. උදාහරණයක් විදියට, Table එකක් හදන එක Changeset එකකින්, ඒකට Column එකක් එකතු කරන එක තව Changeset එකකින් කරන්න. ඒ නිසා Changesets පොඩියට තියාගන්න. මේකෙන් Change එකක් ආවොත් Identify කරන්න වගේම Rollback කරන්නත් ලේසියි.
  • Descriptive Changeset IDs: Changeset එකේ id එකට හොඳ විස්තරාත්මක නමක් දෙන්න. (උදා: 20231027-create-users-table, add-email-column-to-users). මේකෙන් Changelog එක කියවද්දි මොකක්ද මේ Changeset එකෙන් වෙන්නේ කියලා තේරුම් ගන්න ලේසියි.
  • Preconditions පාවිච්චි කරන්න: Preconditions කියන්නේ Changeset එකක් Run වෙන්න කලින් Check කරන Condition එකක්. උදාහරණයක් විදියට, Table එකක් දැනටමත් තියෙනවා නම් Changeset එක Run වෙන්න එපා කියලා කියන්න පුළුවන්. මේකෙන් අනවශ්‍ය දේවල් Run වෙන එක වළක්වා ගන්න පුළුවන්. <preconditions> <not> <tableExists tableName="my_table"/> </not> </preconditions> වගේ දේවල් දාන්න පුළුවන්.
  • Rollback Attributes: හැම Changeset එකකටම අදාළ Rollback Command එකක් දාන්න පුළුවන් නම් හොඳයි. සමහර Change Types වලට Liquibase එකෙන් Auto Rollback එකක් දෙනවා. නැත්නම් <rollback> Tag එක පාවිච්චි කරලා අදාළ SQL Queries දාන්න පුළුවන්. උදාහරණයක් විදියට addColumn එකකට dropColumn එක Rollback විදියට දාන්න පුළුවන්.
  • Development Flows වලදි: Developersලා සාමාන්‍යයෙන් තමන්ගේ Local Database එක Clean කරලා හැම තිස්සෙම Liquibase Run කරන එකෙන් Database Schema එක හැමෝටම Consistent වෙනවා. අලුත් Changes Push කරද්දි, අනිත් Developersලාට ඒ Changes Pull කරලා තමන්ගේ Local Database එක Update කරගන්න පුළුවන්. මේකෙන් 'It works on my machine' ප්‍රශ්නයට විසඳුමක් ලැබෙනවා.
  • Version Control Integration: හැම Changeset File එකක්ම Project Code එකත් එක්කම Version Control (Git වගේ) එකට දාන්න ඕනේ. ඒ නිසා Database Changes, Application Code Changes එක්කම Manage වෙනවා.
  • Test කරන්න: හැම Migrations එකක්ම Test Environments වල හරියට Run වෙනවාද කියලා Check කරන්න. Production වලට යන්න කලින් හොඳට Test කරන එක අත්‍යවශ්‍යයි.

මචංලා, මේ Tips ටික මතක තියාගන්න. මේවා Project එක සාර්ථකව කරගෙන යන්න ගොඩක් වැදගත් වෙයි.

අවසානයට

අපි අද Spring Boot Application එකක් එක්ක Liquibase පාවිච්චි කරලා Database Schema Changes Manage කරන හැටි ගැන මුල ඉඳන්ම කතා කළා. Database Versioning කියන්නේ Software Development Process එකේ අත්‍යවශ්‍යම කොටසක්. Liquibase වගේ Tool එකක් පාවිච්චි කිරීමෙන් ඔයාලගේ Team එකට Database Changes Manage කරන එක ගොඩක් පහසු වෙනවා. ඒ වගේම Project වල Quality එක වැඩි වෙනවා වගේම Development Cycle එකත් වේගවත් වෙනවා.

මේ ලිපියෙන් ඔයාලට Liquibase ගැන හොඳ අවබෝධයක් ලැබෙන්න ඇති කියලා හිතනවා. දැන් ඔයාලට පුළුවන් මේක ඔයාලගේ Project වලට එකතු කරලා අත්හදා බලන්න. මොකද, theory විතරක් මදි, practice තමයි වැඩේ. ඔයාලගේ අත්දැකීම් කොහොමද? Liquibase පාවිච්චි කරද්දි ප්‍රශ්න ඇති වුනාද? එහෙම නැත්නම් මේක ගැන තව දැනගන්න ඕනෑ දේවල් තියෙනවද? පහලින් Comment එකක් දාන්න. අපි ඒ ගැන කතා කරමු.

ආයෙත් මේ වගේ Technical Blog Post එකකින් හම්බවෙමු!