Most of the applications I've come across store data in a SQL database. If you have a corporate application, then most likely there are several stands: a development stand, pre-prod and prod. And a team of developers is working on the application.
Such applications face the problem of synchronizing the database schema between the circuits and the developers themselves. It is necessary to somehow transfer the changes that you made to everyone else and at the same time not get conflicts.
These problems are solved by the Liquibase migration management system. It is a kind of version control system for your database.
Liquibase is a database independent library for tracking, managing and applying changes to a database schema.
Changes for the database are recorded in a format understandable by Liquibase, and it, in turn, executes queries to the database. Thus, independence from a specific database is realized. Liquibase supports 10 database types including DB2, Apache Derby, MySQL, PostgreSQL, Oracle, Microsoft® SQL Server, Sybase, and HSQL. A list of all supported databases can be found on the website .
There are other migration management systems: Doctrine 2 migrations, Rails AR migrations, DBDeploy, etc. But some of them are platform-dependent, some do not have such a wide functionality.
Also, a serious drawback of many systems is the impossibility of applying some changes without losing data, for example, renaming a column will occur as two operations: drop + add, which will lead to data loss.
How Liquibase Works
Liquibase is a cross-platform Java application, which means you can download a JAR file and use it on Windows, Mac or Linux.
As an example, we will consider working with a spring-boot application and a PostgresSQL database. But you should be aware that liquibase can also be used separately as a .jar file. Like this:
java -jar liquibase.jar --driver=com.mysql.jdbc.Driver--classpath=lib/mysql-connector-java-5.1.21-bin.jar --changeLogFile=/path/to/changelog.yaml --url="jdbc:mysql://localhost/application" --username=dbuser --password=secret update
Changelog
Changes to the database structure are written to files called changelog. Supported formats: XML, YAML, JSON or SQL.
Change files can be arbitrarily included in each other for better control. More on this below.
XML , .
ChangeSet
ChangeSet – , Git. ChangeSet . ChangeSet.
changeSet id
, author
filename
, .
Liquibase :
databasechangelog
– . changeSet.
databasechangelock
– , Liquibase.
Liquibase , . , , Liquibase .
Liquibase databasechangelock
, boolean locked
. Liquibase , true
, false
.
, Liquibase , false
. :
, databasechangelock
locked
false
.
Liquibase changelog, , .
changeSet databasechangelog
MD5 changeSet. XML.
Liquibase , . changeSet, , .
changeset
spring-boot , Liquibase.
GitHub:
https://github.com/Example-uPagge/liqubase
spring-boot
Liquibase, maven:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
application.yml
:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/liquibase_example
username: postgres
driver-class-name: org.postgresql.Driver
password: password
Hibernate, .
changelog. spring-boot Liquibase resources/db/changelog/db.changelog-master.yml
. XML .
resources/db/changelog/db.changelog-master.xml
. application.yml
:
spring:
# .. .. .. .. ..
liquibase:
change-log: classpath:db/changelog/db.changelog-master.xml
:
<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">
// changeSets
</databaseChangeLog>
, changeSet , .
Person.
<changeSet id="create-table-person" author="uPagge">
<createTable tableName="person">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="telegram_id" type="int">
<constraints unique="true"/>
</column>
</createTable>
</changeSet>
createTable
tableName
, . , .
. Liquibase, .
id. , constraints
:
primaryKey="true"
– .
nullable="false"
– NULL.
primaryKey nullable . H2 , - .
spring-boot 3 , person
.
changeSet. :
<changeSet id="create-table-person" author="uPagge">
<createTable tableName="person">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="telegram_id" type="int">
<constraints unique="true"/>
</column>
<column name="address" type="varchar(300)"/>
</createTable>
</changeSet>
.
changeSet , databasechangelog
, changeSet. git .
:
changeSet . []
Liquibase.
changeSet
databasechangelog
. , changeSet - . .
changeSet :
<changeSet id="create-table-person" author="uPagge">
<createTable tableName="person">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="telegram_id" type="int">
<constraints unique="true"/>
</column>
</createTable>
</changeSet>
<changeSet id="add-new-column-address" author="uPagge">
<addColumn tableName="person">
<column name="address" type="varchar(300)"/>
</addColumn>
</changeSet>
. , .
. Book
Person
. changeSet:
<changeSet id="create-table-book" author="uPagge">
<createTable tableName="book">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="author_id" type="int">
<constraints foreignKeyName="book_author_id_person_id" references="person(id)"/>
</column>
</createTable>
</changeSet>
author_id
id
Person
.
foreignKeyName
. : _ + _ + __ + ___.
:
<constraints foreignKeyName="book_author_id_person_id" references="person(id)" deleteCascade="true"/>
, , .
, :
<changeSet id="create-table-book" author="uPagge">
<createTable tableName="book">
<column name="id" type="int" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)"/>
<column name="author_id" type="int"/>
</createTable>
<addForeignKeyConstraint baseTableName="book" baseColumnNames="author_id"
constraintName="book_author_id_person_id"
referencedTableName="person"
referencedColumnNames="id"
onUpdate="CASCADE"/>
</changeSet>
, XML, SQL:
<changeSet id="create-view-book-author" author="uPagge">
<createView viewName="author_and_book">
SELECT p.id as person_id,
p.name as person_name,
b.id as book_id,
b.name as book_name
FROM person p
LEFT JOIN book b on p.id = b.author_id
</createView>
</changeSet>
Liquibase 2017. Liquibase , .
changeSet, SQL. , .
ChangeSet
changeSet changelog : . , , changeSet.
ChangeLog . changelog.
:
db/changelog
. ,v.1.0.0
.
-.
cumulative.xml
.
, changelog
cumulative.xml
.
db.changelog-master.xml
cumulative.xml
.
, changeSet. changeLogs changeLogs .
, , id
changeSet.
, :
changelog,
cumulative.xml
, , . :2020-03-08-create-tables.xml
id
changeSet.id="2020-03-08-create-table-person"
.
Liquibase . Liquibase.
- , .
, .
XML
«» XML, DSL: groovy, yaml, json.
, :
IDE
remark
, . remark
.
<changeSet id="2021-02-22-create-person" author="uPagge">
<createTable tableName="person" remarks=" ">
<column name="id" type="int" autoIncrement="true" remarks="">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="name" type="varchar(64)" remarks=" ">
<constraints nullable="false"/>
</column>
<column name="telegram" type="int" remarks=" ">
<constraints unique="true"/>
</column>
</createTable>
</changeSet>
: Liquibase.
Liquibase, . , Liquibase.
, .