Database Schema Migration

Database schema migration, or database migration for short, is a process of updating the database schema in accordance with changes of your application data model.

Jmix uses Liquibase as a database migration tool.

Workflow

Jmix Studio creates migration scripts in the Liquibase changelog format by comparing the current state of your JPA entities with the current database schema of the corresponding data store. It happens automatically when you start the application using a Run/Debug Configuration. The changelog is saved in the source code of the application and is built into the binary artifact.

You can disable creation of migration scripts by editing the Run/Debug configuration settings.

When the application starts, it automatically runs Liquibase with the generated changelogs to update the connected database.

You can also create a changelog at any time using the Generate Diff Changelog action and execute it using the Update action on a data store in the Studio Jmix tool window.

Add-ons that your application depends on can also contain Liquibase changelog files. Changelogs from dependencies are executed on your database before the ones of the application.

Changelog Files Structure

Studio generates changelog files in the src/main/resources/<base_package>/liquibase directory of the application project. In this directory, Studio creates the changelog directory for the main data store, and <store>-changelog directories for additional data stores. Changelog files are created in subdirectories corresponding to the year and month of the current date. Name of each file includes a current day, a sequence number within the day, and a random character sequence to avoid conflicts if multiple developers work on the same project.

src/main/resources/com/company/demo/
├── liquibase/ (1)
│   ├── changelog/ (2)
│   │   ├── 010-init-user.xml (3)
│   │   └── 2020/
│   │       ├── 11/
│   │       │   ├── 12-010-fe2b82e6.xml (4)
│   │       │   └── 27-010-fe2b82e6.xml
│   │       └── 12/
│   │           └── 17-010-fe2b82e6.xml
│   ├── changelog.xml (5)
│   ├── locations-changelog/ (6)
│   │   └── 2020/
│   │       └── 11/
│   │           ├── 25-010-fe2b82e6.xml
│   │           └── 28-010-fe2b82e6.xml
│   └── locations-changelog.xml (7)
1 The root of Liquibase changelog files structure.
2 Changelogs directory of the main data store.
3 Changelog file provided in the new project. It creates a table for the User entity.
4 Changelog files generated by Studio.
5 Root changelog file of the main data store.
6 Changelogs directory of the locations data store.
7 Root changelog file of the locations data store.

For each data store, there is a root changelog file located in the src/main/resources/<base_package>/liquibase directory. It contains a directive to include all generated changelog files under the changelog subdirectory. The project must have the <data-store-name>.liquibase.change-log property set to the root changelog path, for example:

main.liquibase.change-log = com/company/demo/liquibase/changelog.xml

The root Liquibase changelog for the main data store contains include directives for changelogs of all add-ons used in the project. It makes the changelog compatible with the Liquibase Gradle plugin and Liquibase CLI and allows you to run Liquibase outside the Studio or the application, for example, on a CI server.

Studio maintains the includes automatically when you add or remove add-ons. Also, when you start the application, it checks that includes in the root changelog correspond to the add-ons used in the project. If it finds a mismatch, Studio shows a notification dialog and suggests adding or removing includes. You can ignore some paths, and the notification won’t be shown again for the ignored items.

You can write Liquibase changelog files manually and put them into the structure described above. Liquibase executes included changelogs in the alphabetical order considering the whole path, so give your files appropriate names.

Never remove the root changelog.xml file, it is required to start the migration process. Also, don’t remove changelog/010-init-user.xml unless you want to implement security in a non-standard way.

Configuring Liquibase

You can specify Liquibase properties for the main data store in application.properties in the same way as described in the Spring Boot documentation (see spring.liquibase.* properties), but replacing the spring.liquibase prefix with main.liquibase, for example:

main.liquibase.enabled = false

For an additional store, replace main in the property name with the name of the data store.

application.properties must contain the explicit path to the root Liquibase changelog for each data store. For example:

main.liquibase.change-log=com/company/demo/liquibase/changelog.xml

second.liquibase.change-log=com/company/demo/liquibase/second-changelog.xml

By default, Jmix Studio compares all tables of the database with the data model, and generates migration scripts including DROP TABLE statements for tables that are not mapped to entities. If your database contains tables not associated with the application entities, you can ignore these tables by listing their prefixes or entire names in the main.datasource.studio.liquibase.exclude-prefixes application property. For example:

main.datasource.studio.liquibase.exclude-prefixes = abc_,foo,bar