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

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

For each data store, there is a master 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 master changelog file is passed to Liquibase when the application or Studio runs the database migration.

src/main/resources/datamodel/ex1/
├── 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 Master changelog file of the main data store.
6 Changelogs directory of the locations data store.
7 Master changelog file of the locations data store.

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 master 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 jmix.liquibase, for example:

jmix.liquibase.enabled = false
You cannot change the change-log property because it is always set to the master changelog by the framework.

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.excludePrefixes application property. For example:

main.datasource.studio.liquibase.excludePrefixes = abc_,foo,bar

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

If you want to externalize Liquibase properties of an additional data store, change its configuration as in the following example:

@Bean
@ConfigurationProperties(prefix = "locations.liquibase")
public LiquibaseProperties locationsLiquibaseProperties() {
    return new LiquibaseProperties();
}

@Bean
public SpringLiquibase locationsLiquibase(LiquibaseChangeLogProcessor processor) {
    return JmixLiquibaseCreator.create(locationsDataSource(),
            locationsLiquibaseProperties(), processor, "locations");
}

As a result, you will be able to set Liqubase properties of the locations data store in application.properties with the locations.liquibase prefix.