-
Notifications
You must be signed in to change notification settings - Fork 10
SADU Updater
SADU provides a database updater which deploys patch to your database. Which scripts are deployed is defined by a database version in the database itself and in your project.
The patches are semi failsave. If one of the queries fails the upgrade will be aborted. This avoids that the database ends up in a partially migrated state.
dependencies {
implementation("de.chojo.sadu", "sadu-updater", "<version>")
}
In order to create a updater you will need a sql type. These types are provided by our database implementations. Make sure to import the one you need.
Once you imported it you can use the type class which has the same name like your database.
Setting up the updater is quite straight forward.
Create a structure like this in your resources. We will configure our updater to update a postgres database.
resources
└── database # The database directory holds all information
├── postgresql # All script for postgresql are stored here
│ └── 1 # All scripts for 1.x are contained in this directory
│ └── setup.sql # The initial setup of the database
└── version # The current database version. Currently 1.0
Of course you can add scripts for multiple databases. All database directories need all setup and other scripts.
To set the version simply enter a major and patch version separated by a dot in your version file.
1.0
Enter all your queries for the initial database setup into the setup.sql file.
For our example we add a simple table we want to deploy:
CREATE TABLE dev_schema.test
(
id SERIAL,
name TEXT,
age INTEGER
); -- Make sure to end your statement with a semicolon
So execute the updater, we need to create a instance of it, pass a datasource and execute it after some minimal configuration.
class Updater {
private final DataSource dataSource;
Updater(DataSource dataSource) {
this.dataSource = dataSource;
}
public void update() throws IOException, SQLException {
// We create our updater for the postgres database.
// By setting the type our updater knows which directory it needs to search for our scripts.
SqlUpdater.builder(dataSource, PostgreSql.get())
// For easier development on our dev system we use a dev_schema in our database
// During deployment we want to change the name to live_schema
.setReplacements(new QueryReplacement("dev_schema.", "live_schema."))
// Our version should be stored in this table. Changing the table later can cause trouble.
// By default the table name will be only "version"
.setVersionTable("my_app_version")
// We want to deploy to the live_schema.
// By defining it here, the updater will create this schema if it doesnt exist
.setSchemas("live_schema")
// In the end we execute out update.
.execute();
}
}
What happens now?
On the first startup the updater will do the following: 0. Check if the schema exists and creates it if missing. (This only works on databases which actually support schemas)
- Create a version table
- Execute the setup.sql file
- Set the database version to 1.0
If you want to deploy changes on an existing database you need to add a patch. At some point you will have multiple patches for your database. After some time your directory will look like this:
resources
database
postgresql
1
setup.sql # Setup for 1.0
patch_1.sql # Patch for 1.1
patch_2.sql # Patch for 1.2
patch_3.sql # Patch for 1.3
version # The version is now 1.3
What happens now if we execute the updater on the database with version 1.0 we created previously?
- Compare the version of the version table and our version file.
- Load all patches after 1.0 (Basically all patch_x.sql) files
- Execute them one after another.
At some point you probably want a fresh start and not deploy 20 patches everytime your setup your database again.
Thats where the migration files comes in handy. Add a new version directory /2/
. Add a migrate.sql
script to
your old version. Add a /2/setup.sql
script which represents the state of the database after a successful migration.
resources
database
postgresql
1
setup.sql
patch_1.sql
patch_2.sql
patch_3.sql
patch_4.sql # Patch for 1.4
migrate.sql # Migration from 1.4 to 2.0
2
setup.sql # Setup for 2.0
patch_1.sql # Patch for 2.1
version # The version is now 2.1
This is what the updater will to when encountering a database with version 1.3.
- Compare the versions
- Load all patches after the 1.3 patch (
/1/patch_4.sql
) - Execute the patches
- Load the
/1/migrate.sql
script - Execute the migration
- Load all patches after 2.0 (
/2/patch_1.sql
) - Execute all patches
Note 1: The /1/migrate.sql
needs to create the same state as the /2/setup.sql
Note 2: When a basic setup would be performed now, the updater would skip everything in /1/
and directly deploy
/2/setup.sql
.