Liquibase問題
隨着項目的發展,一個項目中的代碼量會非常龐大,同時數據庫表也會錯綜復雜。如果一個項目使用了Liquibase對數據庫結構進行管理,越來越多的問題會浮現出來。
- ChangeSet文件同時多人在修改,自己的ChangeSet被改掉,甚至被刪除掉。
- 開發人員將ChangeSet添加到已經執行過的文件中,導致執行順序出問題。
- 開發人員擅自添加對業務數據的修改,其它環境無法執行並報錯。
- ChangeSet中SQL包含schema名稱,導致其它環境schema名稱變化時,ChangeSet報錯。
- 開發人員不小心改動了已經執行過的ChangeSet,在啟動時會報錯。
Liquibase基本規范
- ChangeSet id使用[任務ID]-[日期]-[序號],如 T100-20181009-001
- ChangeSet必須填寫author
- Liquibase禁止對業務數據進行sql操作
- 使用
<sql>
時,禁止包含schema名稱 - Liquibase禁止使用存儲過程
- 所有表,列要加remarks進行注釋
- 已經執行過的ChangeSet嚴禁修改。
- 不要隨便升級項目liquibase版本,特別是大版本升級。不同版本ChangeSet MD5SUM的算法不一樣。
其它數據庫規范不再贅述。
<?xml version="1.0" encoding="UTF-8"?> <databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"> <changeSet id="T100-20181009-001" author="markfredchen" > <createTable tableName="demo_user" remarks="用戶表"> <column name="id" type="bigint" remarks="用戶ID,主鍵"> <constraints nullable="false" primaryKey="true" primaryKeyName="pk_demo_user_id"/> </column> <column name="username" type="varchar(100)" remarks="用戶名"> <constraints nullable="false"/> </column> ... </createTable> </changeSet> </databaseChangeLog>
有效文件管理
使用Liquibase中提供<include file="xxx"/>
tag,可以將ChangeSet分布在不同文件中。同時<include/>
支持多級引用。
基於此功能可以對項目中的ChangeSet進行有效管理。推薦使用以下規范進行管理。
根據發布進行管理
- 每個發布新建一個文件夾,所有發布相關的ChangeSet文件以及數據初始化文件,均放在些文件夾中。
- 每個發布新建一個master.xml。此master.xml中,include本次發布需要執行的ChangeSet文件
- 根據開發小組獨立ChangeSet文件(可選)
- 根據功能獨立ChangeSet文件。例如user.xml, company.xml
resources
|-liquibase |-user | |- master.xml | |- release.1.0.0 | | |- release.xml | | |- user.xml -- 用戶相關表ChangeSet | | |- user.csv -- 用戶初始化數據 | | |- company.xml -- 公司相關表ChangeSet | |- release.1.1.0 | | |- release.xml | | |- ...
模塊化管理
當項目變得龐大之后,一個服務可能包含的功能模塊會越來越多。此時大家會想盡辦法進行模塊拆分,逐步進行微服務化。然而在面對錯綜復雜的Liquibase ChangeSet就會無從下手。
針對這種將來可能會面對的問題,項目初期就對Liquibase進行模塊化管理,將在未來帶來很大收益。
首先說明一下Spring Boot中Liquibase默認是如何執行以及執行結果。
- 在啟動時,LiquibaseAutoConfiguration會根據默認配置初始化SpringLiquibase
- SpringLiquibase.afterPropertiesSet()中執行ChangeSet文件
- 第一次跑ChangeSets的時候,會在數據庫中自動創建兩個表
databasechangelog
和databasechangeloglock
因此我們可以認為一個SpringLiquibase執行為一個模塊。
引入多模塊管理時,基於上節文件管理規范,我們基於模塊管理再做下調整。
resources
|-liquibase |-user | |- master.xml | |- release.1.0.0 | | |- release.xml | | |- user.xml -- 用戶相關表ChangeSet | | |- user.csv -- 用戶初始化數據 | | |- company.xml -- 公司相關表ChangeSet | |- release.1.1.0 | | |- release.xml | | |- ... |- order | |- master.xml | |- release.1.0.0 | | |- ...
當有一天我們需要把訂單模塊拆分成獨立服務時,我們只需要將模塊相關的ChangeSet文件遷出來。即可完成數據結構的拆分。
那如何在一個Spring Boot運行多個SpringLiquibase呢?需要對代碼進行以下調整。
- 禁用Spring Boot自動運行Liquibase。
當以下配置被啟用時,Spring Boot AutoConfigure會使用默認配置初始化名為springLiquibase的Bean。然后我們不對其進行配置,Spring Boot啟動時會報錯。
# application.properties # spring boot 2以上 spring.liquibase.enabled=false # spring boot 2以下 liquibase.enabled=false
- Spring Boot配置Liquibase Bean
配置兩個SpringLiquibase Bean,Bean名稱分別為userLiquibase和orderLiqubase。
@Configuration public class LiquibaseConfiguration() { /** * 用戶模塊Liquibase */ @Bean public SpringLiquibase userLiquibase(DataSource dataSource) { SpringLiquibase liquibase = new SpringLiquibase(); // 用戶模塊Liquibase文件路徑 liquibase.setChangeLog("classpath:liquibase/user/master.xml"); liquibase.setDataSource(dataSource); liquibase.setShouldRun(true); liquibase.setResourceLoader(new DefaultResourceLoader()); // 覆蓋Liquibase changelog表名 liquibase.setDatabaseChangeLogTable("user_changelog_table"); liquibase.setDatabaseChangeLogLockTable("user_changelog_lock_table"); return liquibase; } /** * 訂單模塊Liquibase */ @Bean public SpringLiquibase orderLiquibase() { SpringLiquibase liquibase = new SpringLiquibase(); liquibase.setChangeLog("classpath:liquibase/order/master.xml"); liquibase.setDataSource(dataSource); liquibase.setShouldRun(true); liquibase.setResourceLoader(new DefaultResourceLoader()); liquibase.setDatabaseChangeLogTable("order_changelog_table"); liquibase.setDatabaseChangeLogLockTable("order_changelog_lock_table"); return liquibase; } }