Liquibase یک Version Control برای دیتابیس است که تغییرات اسکیمای دیتابیس را براحتی قابل رصد، نسخه گذاری و توسعه پذیر می کند
ابتدا کتابخانه آنرا اضافه میکنیم :
<dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-core</artifactId> <version>3.4.1</version> </dependency>
برای نسخه های جدیدتر به این لینک مراجعه کنید
قسمت اصلی که Liquibase از آن استفاده میکند فایل ChangeLog است که ساختار xml ای دارد و کلیه تغییرات را با جزییات در آن نگهداری میکند که نگاهی به ساختار آن می اندازیم :
<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"> <changeSet author="John" id="someUniqueId"> <addColumn tableName="users"> <column name="address" type="varchar(255)" /> </addColumn> </changeSet> </databaseChangeLog>
نمونه بالا نشان میدهد که یک Column با نام address به جدول users توسط john و Id مشخصش اضافه شده است
اجرای Liquibase بوسیله Spring Bean :
نخستین راه برای اجرا کردن تغییرات Liquibase در Spring استفاده از Bean است البته راه های دیگری وجود دارد ولی در Spring ساده ترین راه استفاده در قالب Bean است
@Bean public SpringLiquibase liquibase() { SpringLiquibase liquibase = new SpringLiquibase(); liquibase.setChangeLog("classpath:liquibase-changeLog.xml"); liquibase.setDataSource(dataSource()); return liquibase; }
* باید توجه داشته باشیم که فایل changelog.xml در مسیر معرفی شده وجود داشته باشد
استفاده از Liquibase با Spring Boot :
اگر Liquibase را با Spring Boot استفاده کنیم نیازی نداریم که Bean تعریف کنیم فقط باید کتابخانه آنرا اضافه کنیم و فایل changelog را در مسیر db/changelog/db.changelog-master.yaml قرار دهیم و بصورت اتوماتیک شناسایی میشود
مسیر فایل changelog را میتوان در فایل تنظیمات Properties تغییر داد:
liquibase.change-log=classpath:liquibase-changeLog.xml
غیر فعال سازی Liquibase در Spring Boot :
گاهی نیاز داریم Liquibase را غیر فعال کنیم راحتترین راه ست کردن آن در Properties است
در Spring 2 :
spring.liquibase.enabled=false
در Spring 1 :
liquibase.enabled=false
ساخت فایل changelog با کمک Maven Plugin :
بجای اینکه فایل changelog را بصورت دستی بنویسیم میتوانیم از پلاگین Maven برای ایجاد این فایل کمک بگیریم و در زمان صرفه جویی کنیم
اضافه کردن وابستگی برای plugin :
<dependency> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.4.1</version> </dependency> ... <plugins> <plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.4.1</version> <configuration> <propertyFile>src/main/resources/liquibase.properties</propertyFile> </configuration> </plugin> </plugins>
و بعد برای ساخت changelog از دیتابیس موجود از این پلاگین استفاده کنیم :
mvn liquibase:generateChangeLog
محتویات فایل property :
url=jdbc:mysql://localhost:3306/oauth_reddit username=tutorialuser password=tutorialmy5ql driver=com.mysql.jdbc.Driver outputChangeLogFile=src/main/resources/liquibase-outputChangeLog.xml
فایل changelog ساخته شده بعد از اجرا دستور بالا :
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <databaseChangeLog ...> <changeSet author="John (generated)" id="1439225004329-1"> <createTable tableName="APP_USER"> <column autoIncrement="true" name="id" type="BIGINT"> <constraints primaryKey="true"/> </column> <column name="accessToken" type="VARCHAR(255)"/> <column name="needCaptcha" type="BIT(1)"> <constraints nullable="false"/> </column> <column name="password" type="VARCHAR(255)"/> <column name="refreshToken" type="VARCHAR(255)"/> <column name="tokenExpiration" type="datetime"/> <column name="username" type="VARCHAR(255)"> <constraints nullable="false"/> </column> <column name="preference_id" type="BIGINT"/> <column name="address" type="VARCHAR(255)"/> </createTable> </changeSet> ... </databaseChangeLog>
حالا از این فایل برای ساخت دیتابیس یا ایجاد تغییرات میتوانیم استفاده کنیم
استفاده از Plugin برای ساخت changeLog بین تفاوت دو دیتابیس :
فرض کنید میخواهیم فایل changelog ای داشته باشیم که در آن تفاوت های دو دیتابیس (مثلا development و production) :
mvn liquibase:diff
فایل تنظیمات properties :
changeLogFile=src/main/resources/liquibase-changeLog.xml url=jdbc:mysql://localhost:3306/oauth_reddit username=tutorialuser password=tutorialmy5ql driver=com.mysql.jdbc.Driver referenceUrl=jdbc:h2:mem:oauth_reddit diffChangeLogFile=src/main/resources/liquibase-diff-changeLog.xml referenceDriver=org.h2.Driver referenceUsername=sa referencePassword=
و در فایل changelog تغییرات پیدا شده را ثبت کرده ایم :
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <databaseChangeLog ...> <changeSet author="John" id="1439227853089-1"> <dropColumn columnName="address" tableName="APP_USER"/> </changeSet> </databaseChangeLog>
این امکان قدرتمندی برای تکامل دیتابیس است مثلا به hibernate اجازه میدهد که اسکیمای جدیدی برای حالت development ایجاد کند و به عنوان یک مرجع در مقابل تغییرات استفاده کند
استفاده از Hibernate Liquibase Plugin :
اگر برنامه از Hibernate استفاده میکنیم میتوانیم از Hibernate Liquibase Plugin استفاده کنیم
ابتدا تنظیمات و وابستگی های انرا ست میکنیم :
<plugins> <plugin> <groupId>org.liquibase</groupId> <artifactId>liquibase-maven-plugin</artifactId> <version>3.4.1</version> <configuration> <propertyFile>src/main/resources/liquibase.properties</propertyFile> </configuration> <dependencies> <dependency> <groupId>org.liquibase.ext</groupId> <artifactId>liquibase-hibernate4</artifactId> <version>3.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.7.3.RELEASE</version> </dependency> </dependencies> </plugin> </plugins>
تولید changelog بین تفاوت های Entity ها و Database :
هنگامی که یک Entity تغییری ایجاد شد میتوانیم با استفاده از این پلاگین تفاوت های انرا با دیتابیس log بگیریم :
changeLogFile=classpath:liquibase-changeLog.xml url=jdbc:mysql://localhost:3306/oauth_reddit username=tutorialuser password=tutorialmy5ql driver=com.mysql.jdbc.Driver referenceUrl=hibernate:spring:org.baeldung.persistence.model ?dialect=org.hibernate.dialect.MySQLDialect diffChangeLogFile=src/main/resources/liquibase-diff-changeLog.xml
دقت کنید که referenceUrl از package scan استفاده میکند و در ادامه باید نوع Dialect مورد استفاده را هم قید کنیم
ویژگی Rollback در Liquibase :
در شرایط بحرانی گاهی ما نیاز داریم عملیات انجام شده در Liquibase را به حالت قبل برگردانیم
دو نوع عملیات در Liquibase وجود دارد که منجر به Rollback شدن میشود
- Automatic : هنگامی که مهاجرت به دیتابیس جدید نیاز به Rollback شدن پیدا کند
- Manual : هنگامی که ما تصمیم به Rollback میگیریم چون عملیات مهاجرت به دیتابیس موفقیت آمیز نیست
مثلا rollback شدن دستور create table ممکن است table را drop کند و در اینجا rollback بصورت اتوماتیک رخ میدهد
یا rollback شدن دستور drop table امکانپذیر نباشد چون آخرین وضعیت جدول مشخص نیست و در این حالت نیاز است rollback بصورت manual یا دستی انجام شود
نوشتن Rollback ساده :
با استفاده از تگ changeset میتوانیم گروه تغییراتی که نیاز است در دیتابیس اعمال شود را مشخص کنیم
که در اینجا تصمیم داریم عمل Rollback را انجام دهیم :
<changeSet id="testRollback" author="baeldung"> <createTable tableName="baeldung_turorial"> <column name="id" type="int"/> <column name="heading" type="varchar(36)"/> <column name="author" type="varchar(36)"/> </createTable> <rollback> <dropTable tableName="baeldung_test"/> </rollback> </changeSet>
مثال بالا در گروه اول قرار میگیرد اگر موردی به آن اضافه نکنیم عمل rollback بصورت اتوماتیک انجام میشود اما ما میتوانیم حالت پیش فرض آنرا عوض کنیم
برای اجرای دستور مهاجرت :
mvn liquibase:update
و بعد عمل rollback را انجام میدهیم :
mvn liquibase:rollback
طبق چیزی که تعریف کردیم این دستور تغییرات موجود در تگ changeset را rollback میکند و عملیاتی که طی update انجام شده را برمیگرداند اما rollback شدن با شکست روبرو خواهد شد چون ما نقطه rollback شدن را تعریف نکردیم و بازگشت به مراحل اولیه دیتابیس را کاملا پاک میکند تا به مرحله اولیه آن بازگردد پس باید برای Rollback شدن حد و نقطه آنرا تعیین کنیم که سه شرط زیر لازم است :
rollbackTag
rollbackCount
rollbackDate
شرایط rollbackTag :
ما میتوانیم وضعیت خاصی از دیتابیس را به عنوان یک tag تعیین کنیم و بعدا به آن تگ که نماینده وضعیت خاص مد نظر ماست که قبلا تعیین کردیم Rollback کنیم
mvn liquibase:rollback -Dliquibase.rollbackTag=1.0
تگ تعریف شده "1.0" بوده که با اجرای دستور بالا تمامی changeset ها به حالتی که در "1.0" وجود داشت برمیگردد
شرایط rollbackCount :
در این حالت تعداد changeset هایی که نیاز است به عقب برگردد را تعیین میکنیم و اگر تعداد را 1 در نظر بگیریم به changeset آخر برمیگردد :
mvn liquibase:rollback -Dliquibase.rollbackCount=1
شرایط rollbackDate :
در این حالت میتوانیم به تغییرات تاریخ خاصی Rollback کنیم
mvn liquibase:rollback "-Dliquibase.rollbackDate=Jun 03, 2017"
* فرمت تاریخ وارد شده ISO است و باید با شرایطی که ()DateFormat.getDateInstance آنرا قبول میکند مطابقت داشته باشد
option های قابل استفاده در Changeset Rollback :
multi statement rollback :
ممکن است بیش از یک دستورالعمل را بخواهیم اجرا کنیم :
<changeSet id="multiStatementRollback" author="baeldung"> <createTable tableName="baeldung_tutorial2"> <column name="id" type="int"/> <column name="heading" type="varchar(36)"/> </createTable> <createTable tableName="baeldung_tutorial3"> <column name="id" type="int"/> <column name="heading" type="varchar(36)"/> </createTable> <rollback> <dropTable tableName="baeldung_tutorial2"/> <dropTable tableName="baeldung_tutorial3"/> </rollback> </changeSet>
Multiple Rollback Tags :
در هر changeset ما میتوانیم بیش از یک تگ rollback داشته باشیم که به ترتیب قرارگیری اجرا میشوند :
<changeSet id="multipleRollbackTags" author="baeldung"> <createTable tableName="baeldung_tutorial4"> <column name="id" type="int"/> <column name="heading" type="varchar(36)"/> </createTable> <createTable tableName="baeldung_tutorial5"> <column name="id" type="int"/> <column name="heading" type="varchar(36)"/> </createTable> <rollback> <dropTable tableName="baeldung_tutorial4"/> </rollback> <rollback> <dropTable tableName="baeldung_tutorial5"/> </rollback> </changeSet>
Refer Another Changeset for Rollback :
ما میتوانیم به changeset دیگر برای rollback شدن ارجاع دهیم. امکان دارد در changeset اصلی بخواهیم جزییاتی از دیتابیس را تغییر دهیم و با اینکار میتوانیم در زمان صرفه جویی کنیم :
<changeSet id="referChangeSetForRollback" author="baeldung"> <dropTable tableName="baeldung_tutorial2"/> <dropTable tableName="baeldung_tutorial3"/> <rollback changeSetId="multiStatementRollback" changeSetAuthor="baeldung"/> </changeSet>
Empty Rollback Tag :
بصورت پیش فرض Liquibase کدی جهت rollback سعی میکند ایجاد کند و اگر بخواهیم Rollback نداشته باشیم میتوانیم یک تگ خالی بگذاریم و عملیاتی برای Rollback اتفاق نخواهد افتاد
<changeSet id="emptyRollback" author="baeldung"> <createTable tableName="baeldung_tutorial"> <column name="id" type="int"/> <column name="heading" type="varchar(36)"/> <column name="author" type="varchar(36)"/> </createTable> <rollback/> </changeSet>
Option های دستورات Rollback :
به غیر از Rollback کردن دیتابیس به وضعیت قبلی، با استفاده از این دستور میتوانیم کارهای نظیر ایجاد Rollback SQL ، ایجاد Rollback Script به وضعیت جلوتر ، تست کردن وضعیت مهاجرت به دیتابیس و Rollback کردن باهم
Generate Rollback Script :
سه گزینه برای ساخت Rollback SQL داریم :
<rollbackSQL <tag : ساخت یک اسکریپت SQL برای Rollback کردن به تگ خاصی
<rollbackToDateSQL <date/time : ساخت یک اسکریپت SQL برای Rollback کردن به تاریخ معینی
<rollbackCountSQL <value : ساخت یک اسکریپت SQL برای Rollback کردن به تعداد مشخصی به قبل
و نحوه اجرای آن :
mvn liquibase:rollbackCountSQL 2
Generate Future Rollback Script :
این حالت به ما اجازه میدهد که همزمان با ایجاد اسکریپت مهاجرت اسکریپت Rollback را هم ایجاد کنیم که بیشتر برای DBA ها مفید خواهد بود ولی برای اطلاعات بیشتر به این لینک مراجعه کنید
mvn liquibase:futureRollbackSQL
Run Update Testing Rollback:
این دستور دیتابیس را آپدیت میکند و بعد تغییرات را به عقب برمیگرداند تا به وضعیت کنونی برسد
mvn liquibase:updateTestingRollback