網頁

2018/3/26

學習Liquibase

最近專案可能會用到Liquibase,所以學習一下Liquibase是什麼。


會用到Liquibase的原因是客戶希望能夠將資料庫中已異動的資料回復成異動前的狀態。

Liquibase官網標題寫"Source Control For Your Database"(資料庫的版本控管),我想應該是對資料庫的設定或內容進行版控。

Liquibase是Apache 2.0 License的開源軟體。

Liquibase的主要概念有Changelog File,Change Set,Changes,Preconditions,Contexts。

Liquibase會使用一個叫changelog的文字檔案(即changelog file),所有資料庫異動都會存在這份檔案中。這份檔案支援XML,YMAL,JSON及SQL格式。

changelog的XML格式使用的XML schema(3.1)如下。

<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>

Change Set是Liquibase的追蹤change執行的單位。ChangeSet使用author,id及changelog file的classpath(or filepath)路徑來做唯一識別。當Liquibase運行時會從DATABASECHAGNELOG資料表查詢標記為已執行的changeSet並與chagelog中的changeSet進行比對,然後執行changelog file中尚未被執行的changeSets。

Change set即是在databaseChangeLog檔中的<changeSet>標籤,範例如下。

<?xml version="1.0" encoding="UTF-8"?>
<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">
    
  <changeSet id="1" author="bob">
    <comment>A sample change log</comment>
    <createTable/>
  </changeSet>
  <changeSet id="2" author="bob" runAlways="true">
    <alterTable/>
  </changeSet>
  <changeSet id="3" author="alice" failOnError="false" dbms="oracle">
    <alterTable/>
  </changeSet>
    
</databaseChangeLog>

id屬性僅用來區別不同的change set而已,並不代表執行順序,也不一定要整數。

當Liquibase執行changelog時,會根據<changeSet>的排列順序查詢DATABASECHAGNELOG資料表檢查是否已經被執行。已執行過的changeSet會被忽略,除非runAlways="true"。當changeSet被執行後,Liquibase會新增一筆包含authur/id/filepath,及changeSet的MD5檢查碼記錄到DATABASECHAGNELOG

Liquibase會試著在一次transaction交易中執行每一個已提交(commit)的changeSet,如果有錯誤發生則會rollback。但部分的資料庫有auto-commit會妨礙這樣的交易設定可能導致無法預期的資料狀態,因此通常最好一個changeSet只有一個change。

<changeSet>的各種屬性說明。

  • id:以數字辨識符(identifier),必要屬性。
  • author:changeSet的作者,必要屬性。
  • dbms:資料庫類型,例如mysql或oracle或mssql,請見支援的資料庫類型
  • runAlways:如果為true則每次changeSet都會被執行,即使之前已經執行過了。
  • runOnChange:如果為true則changSet異動時會被執行。
  • context:在運行時間如果傳入特定的context的異動會執行。
  • runInTransaction:ChangeSet是否可以一次交易來執行,預設為true。。
  • failOnError:執行changeSet時如果錯誤發生migration是否會失敗。

<rollback>設定rollback的方式,可使用SQL敘述,異動標籤或參考前一次的changeSet,範例如下。

使用SQL敘述。

<changeSet id="1" author="bob">
  <createTable tableName="test_table">
  <rollback>
    DROP TABLE TEST_TABLE
  </rollback>
</changeSet>

使用異動標籤<dropTable>

<changeSet id="1" author="bob">
  <createTable tableName="test_table">
  <rollback>
    <dropTable tableName="test_table"/>
  </rollback>
</changeSet>

參考前一次changeSet。

<changeSet id="2" author="bob">
  <dropTable tableName="test_table"/>
  <rollback changeSetId="1" changeSetAuthor="bob"/>
</changeSet>

一個changeSet中通常含有一個change。Liquibase支援敘述式change來產生SQL及原生SQL。通常一個changeSet只會有一個change。


<preConditions>用來設定changeSet的執行條件,只有符合條件的change才會被紀錄。可套在單一個changeSet或整個changelog file。

<?xml version="1.0" encoding="UTF-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.1.xsd">

  <preConditions><!-- (1) -->
    <dbms type="oracle" />
    <runningAs username="SYSTEM" />
  </preConditions>

  <changeSet id="1" author="bob">
    <preConditions onFail="WARN"><!-- (2) -->
        <sqlCheck expectedResult="0">SELECT COUNT(*) FROM OLDTABLE</sqlCheck>
    </preConditions>
    <comment>Comments要放在preCondtion後,否則會Liquibase會跳錯</comment>
    <dropTable tableName="oldtable"/>
  </changeSet>

</databaseChangeLog>

以上的precondition設定有兩個,第一個是讓整個changelog file中的changeSets只有在資料庫為Oracle且使用者為SYSTEM時才會執行。第二個是當查詢OLDTABLE資料筆數為0時才會執行<dropTable>命令。

執行change log的方法包括使用command line指令,Ant,Maven,Spring,Servlet Listener等。


建立一個changelog file,並加入一個changeSet如下

<?xml version="1.0" encoding="UTF-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.1.xsd">

  <changeSet id="1" author="bob">
    <createTable tableName="department">
      <column name="id" type="int">
          <constraints primaryKey="true" nullable="false"/>
      </column>
      <column name="name" type="varchar(50)">
          <constraints nullable="false"/>
      </column>
      <column name="active" type="boolean" defaultValueBoolean="true"/>
    </createTable>
  </changeSet>

</databaseChangeLog>

使用Spring config設定來執行Liquibase設定如下。

<bean id="liquibase" class="liquibase.integration.spring.SpringLiquibase">
  <property name="dataSource" ref="myDataSource" />
  <property name="changeLog" value="classpath:db-changelog.xml" />
  <property name="contexts" value="test, production" /><!--  指定使用的運行環境  -->
</bean>

<bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://localhost:3306/mydatabase" />
  <property name="username" value="matt" />
  <property name="password" value="123" />
</bean>

根據上面的設定,你會看到資料庫中多了一個department資料表及另外兩個資料表DATABASECHANGELOGDATABASECHANGELOGLOCKDATABASECHANGELOG資料表包含了一連串已對資料庫執行的命令敘述。DATABASECHANGELOGLOCK資料表用來確保資料庫不會同時被修改。


大概了解Liquibase透過changelog file來更新資料庫的狀態,例如修改shema,新增資料,刪除資料表等,而每一次的異動(change)都會被記錄在DATABASECHANGELOG,讓我們可回復到某一版本changeSet狀態。

如果是既有的資料庫,則可透過generateChangeLog來產生changelog file,之後就改透過這份changelog file來修改資料庫。

可以參考Liquibase 使用command line操作來玩玩看Liquibase的基本操作。



沒有留言:

張貼留言