Liquibase
provides the generateChangeLog
command to create a ChangeLog
file for an existing database
. By default, the generateChangeLog
command will generate a big ChangeLog
file which will have all the tables, functions, views, columns, indexes, foreign keys, primary keys, unique constraints, data, stored procedures, triggers, sequences
, etc.
but most development teams try to segregate and manage ChangeLog
as per DDL
and DML
for each table into separate files and include them as per order required.
Straightforward there is no way to generate a separate ChangeLog
per table. So the development team first generates ChangeLog
for the entire database
, and then manually tries to segregate them into separate files. but this approach is error-prone
and sometimes creates foreign key voilation
. Let's see the same with an example, assume we have database schema
as below
create DATABASE IF NOT EXISTS `appledb`;
USE `appledb`;
create TABLE `taste` (
`taste_id` int NOT NULL auto_increment,
`taste` varchar(2000) NOT NULL,
PRIMARY KEY (`taste_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
insert into `taste` (`taste`) VALUES ('sweet'),('sour');
create TABLE `apple` (
`apple_id` bigint NOT NULL auto_increment,
`apple_name` varchar(255) DEFAULT NULL,
`taste_id` int NOT NULL,
PRIMARY KEY (`apple_id`),
KEY `FKqg39etajdct3xxd813arauyit` (`taste_id`),
CONSTRAINT `taste_id_taste_fk` FOREIGN KEY (`taste_id`) REFERENCES `taste` (`taste_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
insert into `apple` (`apple_name`, `taste_id` ) VALUES ('Macintosh',1),('Fuji',2);
commit;
Run the above SQL
, and create a database and tables with respective values. once done, let's generate a ChangeLog
for the above data with the help of Liquibase.
liquibase.properties
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
execute the command liquibase generateChangeLog
it will generate a changelog.mysql.xml
file for the entire database.
Manually segrigating the generated ChangeLog
file is very error-prone
.
Generate ChangeLog for each sequence, table and data individually
As stated above, so far there has been no straightforward solution for this problem from Liquiabse
, but there are some workarounds.
Liquibase
provides diffTypes
tag, which will only generate the database object if passed, below are the available diffTypes
diffType | default | description | |
1 | tables | yes | will generate the table creation ChangeLog only |
2 | columns | yes | will generate column addition ChangeLog only |
3 | views | yes | will generate views creations ChangeLog only |
4 | primaryKeys | yes | will generate primary Keys ChangeLog only |
5 | indexes | yes | will generate indexes ChangeLog only |
6 | foreignKeys | yes | will generate foreign keys ChangeLog only |
7 | sequences | yes | will generate sequences ChangeLog only |
8 | data | no | will generate insert data ChangeLog only |
If No diffTypes is mentioned then all the default diffTypes will be considered
Liquibase
provides another includeObjects tag, which takes table and sequence.
By combining diffTypes
and includeObjects as per requirement, we can achieve the desired ChangeLog
file at a time, which will be renamed and another ChangeLog
will be generated, let's see this with an example.
Assume we are working with the same SQL
as used above
create DATABASE IF NOT EXISTS `appledb`;
USE `appledb`;
create TABLE `taste` (
`taste_id` int NOT NULL auto_increment,
`taste` varchar(2000) NOT NULL,
PRIMARY KEY (`taste_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
insert into `taste` (`taste`) VALUES ('sweet'),('sour');
create TABLE `apple` (
`apple_id` bigint NOT NULL auto_increment,
`apple_name` varchar(255) DEFAULT NULL,
`taste_id` int NOT NULL,
PRIMARY KEY (`apple_id`),
KEY `FKqg39etajdct3xxd813arauyit` (`taste_id`),
CONSTRAINT `taste_id_taste_fk` FOREIGN KEY (`taste_id`) REFERENCES `taste` (`taste_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
insert into `apple` (`apple_name`, `taste_id` ) VALUES ('Macintosh',1),('Fuji',2);
commit;
Run the above SQL
, and create a database and tables with respective values. once done, let's generate a ChangeLog
for the above data with the help of Liquibase
.
Generate Taste Table Creation ChangeLog
Let's modify the Liquibase
.properties as below and generate a ChangeLog
for the taste table creation
changeset.
liquibase.properties
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
overwriteOutputFile=true
includeTablespace:true
includeCatalog:true
includeObjects=table:taste
Execute the command liquibase generateChangeLog
it will generate a changelog.mysql.xml
file which will contain create taste table changeset
only, rename this file to changelog.mysql.create.taste.xml
Generate Taste Table Insertion ChangeLog
modify the liquibase.properties
as below and generate a ChangeLog
for the taste table insertion
changeset.
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
overwriteOutputFile=true
includeTablespace:true
includeCatalog:true
diffTypes:data
includeObjects=table:taste
Execute the command liquibase generateChangeLog
it will generate a changelog.mysql.xml
file which will contain insert taste table changeset
only, rename this file to changelog.mysql.insert.taste.xml
Generate Apple Table Creation ChangeLog
modify the liquibase.properties
as below and generate a ChangeLog
for the apple table creation
changeset.
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
overwriteOutputFile=true
includeTablespace:true
includeCatalog:true
includeObjects=table:apple
Execute the command liquibase generateChangeLog
it will generate a changelog.mysql.xml file which will contain the create apple table changeset
only, rename this file to changelog.mysql.insert.taste.xml
Generate Apple Table Insertion ChangeLog
modify the liquibase.properties
as below and generate a ChangeLog
for the apple table insertion
changeset.
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
overwriteOutputFile=true
includeTablespace:true
includeCatalog:true
includeObjects=table:apple
Execute the command liquibase generateChangeLog
it will generate a changelog.mysql.xml
file which will contain insert apple table changeset
only, rename this file to changelog.mysql.insert.apple.xml
Create Master ChangeLog and include all Individual ChangeLog
Now let's create a master changelog.mysql.xml file, which will include all the ChangeLog
we have just created it in the required sequence.
changelog.mysql.xml
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd" objectQuotingStrategy="QUOTE_ALL_OBJECTS">
<include file="changelog.mysql.create.taste.xml" />
<include file="changelog.mysql.insert.taste.xml" />
<include file="changelog.mysql.create.apple.xml" />
<include file="changelog.mysql.insert.apple.xml" />
</databaseChangeLog>
liquibase.properties
changeLogFile=changelog.mysql.xml
liquibase.command.url:jdbc:mysql://localhost:3306/AppleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
liquibase.command.username: root
liquibase.command.password: rootroot
changelog.mysql.create.taste.xml
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="shriksha (generated)" id="1696659398982-1">
<createTable catalogName="appledb" tableName="taste">
<column autoIncrement="true" name="taste_id" type="INT">
<constraints nullable="false" primaryKey="true" />
</column>
<column name="taste" type="VARCHAR(2000)">
<constraints nullable="false" />
</column>
</createTable>
</changeSet>
</databaseChangeLog>
changelog.mysql.insert.taste.xml
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="shriksha (generated)" id="1696659621108-1">
<insert catalogName="appledb" tableName="taste">
<column name="taste_id" valueNumeric="1"/>
<column name="taste" value="sweet"/>
</insert>
<insert catalogName="appledb" tableName="taste">
<column name="taste_id" valueNumeric="2"/>
<column name="taste" value="sour"/>
</insert>
</changeSet>
</databaseChangeLog>
changelog.mysql.create.apple.xml
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="shriksha (generated)" id="1696659529449-1">
<createTable catalogName="appledb" tableName="apple">
<column autoIncrement="true" name="apple_id" type="BIGINT">
<constraints nullable="false" primaryKey="true" />
</column>
<column name="apple_name" type="VARCHAR(255)" />
<column name="taste_id" type="INT">
<constraints nullable="false" />
</column>
</createTable>
</changeSet>
<changeSet author="shriksha (generated)" id="1696659529449-2">
<createIndex catalogName="appledb" indexName="FKqg39etajdct3xxd813arauyit" tableName="apple">
<column name="taste_id" />
</createIndex>
</changeSet>
<changeSet author="shriksha (generated)" id="1696659529449-3">
<addForeignKeyConstraint baseColumnNames="taste_id" baseTableCatalogName="appledb" baseTableName="apple" constraintName="taste_id_taste_fk" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="taste_id" referencedTableCatalogName="appledb" referencedTableName="taste" validate="true" />
</changeSet>
</databaseChangeLog>
changelog.mysql.insert.apple.xml
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" 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/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet author="shriksha (generated)" id="1696659833557-1">
<insert catalogName="appledb" tableName="apple">
<column name="apple_id" valueNumeric="1"/>
<column name="apple_name" value="Macintosh"/>
<column name="taste_id" valueNumeric="1"/>
</insert>
<insert catalogName="appledb" tableName="apple">
<column name="apple_id" valueNumeric="2"/>
<column name="apple_name" value="Fuji"/>
<column name="taste_id" valueNumeric="2"/>
</insert>
</changeSet>
</databaseChangeLog>
execute the command liquibase update
, it will create and insert records in tables as per the below order
- create taste table
- insert record in taste table
- create apple table
- insert record in apple table
The same can be verified by running command
select * from appledb.apple ap, appledb.taste ta where ap.taste_id=ta.taste_id;