Table of Contents
Here, I write how to build application in Spring Boot with DB Migration. I use Kotlin and Harmonica, Kotlin database migration library.
Environment
- Kotlin 1.3.20
- Spring Boot 2.1.2
- Harmonica 1.1.17
- PostgreSQL
- IntelliJ IDEA
- JDK 11 (You can use JDK 8 or later)
Prepare Application Base
First, let’s generate application base on Spring Initializr.
Choose the following component.
- Gradle
- Kotlin
- Spring Boot 2.1.2
Leave the project meta data as it is.
Add the following dependencies.
- Web
- PostgreSQL
- JPA
- Thymeleaf
In old articles, I used kotlinx.html for template, but this time I use Thymeleaf, because most people are familiar with it.
Click “Generate Project” button and extract the zip file, then you can see the file structure as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
demo/ |- build.gradle |- gradle/ | `- wrapper/ | |- gradle-wrapper.jar | `- gradle-wrapper.properties |- gradlew |- gradlew.bat |- settings.gradle `- src/ |- main/ | |- kotlin/ | | `- com/ | | `- example/ | | `- demo/ | | `- DemoApplication.kt | `- resources/ | |- application.properties | |- static/ | `- templates/ `- test/ `- kotlin/ `- com/ `- example/ `- demo/ `- DemoApplicationTests.kt |
Prepare PostgreSQL
Here is the database configuration.
Database name | demo |
---|---|
User name | develoepr |
Password | developer |
Database port | 5432 (Default Port) |
If you don’t have PostgreSQL, you can prepare it with docker as follows.
1 |
docker run -d -p 5432:5432 --name demo -e POSTGRES_USER=developer -e POSTGRES_PASSWORD=developer -e POSTGRES_DB=sample postgres:11 |
If you want to change the listening port, please change the first 5432
to another port number.
Open the Project in IntelliJ IDEA
From the “File” menu, click “Open”, and choose the file, “settings.gradle
“. You will see the dialog that asks whether you open it as a project or not. Then, please click “Open as a Project” button.
After that you will see the dialog for importing, then configure it and click “OK” button.
Well, let’s change the Kotlin version. Open build.gradle
and change Kotlin version to 1.3.20
.
Migration
Add Module
Let’s add the module. In the Project tool window, choose top directory. From the “File” menu, select “New” and “Module”, the you’ll see “New Module” dialog. On the dialog, please choose “Gradle” in the left pane, and choose “Kotlin (Java)” in the right pane, and click “Next” button.
Next, please configure as follows, and click “Next” button and add the module.
Add as module to | demo |
GroupId | com.example (Inherit) |
ArtifactId | db |
Version | 0.0.1-SNAPSHOT (Inherit) |
Auto syncronization will work and you may get the following error output.
1 |
org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':db'. |
Now, open db/build.gradle
and remove the version of the Kotlin plugin and add libraries.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
buildscript { ext { kotlinVersion = '1.3.20' } repositories { jcenter() } dependencies { classpath group: 'org.jetbrains.kotlin', name: 'kotlin-script-util', version: kotlinVersion classpath 'com.improve_future:harmonica:1.1.17' } } plugins { id 'org.jetbrains.kotlin.jvm' } apply plugin: 'jarmonica' group 'com.example' version '0.0.1-SNAPSHOT' repositories { mavenCentral() jcenter() } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" runtimeOnly 'org.postgresql:postgresql:42.2.5' implementation 'com.improve_future:harmonica:1.1.17' implementation group: 'org.reflections', name: 'reflections', version: '0.9.11' } compileKotlin { kotlinOptions.jvmTarget = "1.8" } compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } |
Configuration
Set up the db and directory configuration.
Add the next expression to the bottom of build.gradle
.
1 2 |
extensions.extraProperties["migrationPackage"] = [project.group, project.rootProject.name, project.name].join(".") |
Next, execute the following command.
1 |
mkdir -p db/src/main/kotlin/com/example/demo/db/config |
And create file db/src/main/kotlin/com/example/demo/db/config/Default.kt
as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.example.demo.db import com.improve_future.harmonica.core.DbConfig import com.improve_future.harmonica.core.Dbms config Default : DbConfig({ dbms = Dbms.PostgreSQL dbName = "demo" host = "127.0.0.1" user = "developer" password = "developer" // Add port configuration if you need // port = 5432 }) |
Add and Execute Migration
Now, let’s add the migration. Execute the next command.
1 |
./gradlew jarmonicaCreate -Pname=CreateTask |
Then, you can see the file db/src/main/kotlin/com/example/demo/db/migration/Mxxxxxxxxxxxxxxxxxx_CreateTask.kt
. Change it as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package com.example.demo.db.migration import com.improve_future.harmonica.core.AbstractMigration /** * CreateTask */ class M20190209141813199_CreateTask : AbstractMigration() { override fun up() { createTable("task") { varchar("name", size = 200, nullable = false) dateTime("created_at", nullable = false) dateTime("updated_at", nullable = false) } } override fun down() { dropTable("task") } } |
Execute migration as follows.
1 |
./gradlew jarmonicaUp |
You can see the output like follows.
1 2 3 4 5 |
> Task :db:jarmonicaUp jdbc:postgresql://127.0.0.1:5432/demo?autoReconnect=true == [Start] Migrate up 20190209141813199 == Create Table: task == [End] Migrate up 20190209141813199 == |
See the Database
Let’s see the database with psql
.
1 2 3 4 5 6 7 8 |
demo=# d List of relations Schema | Name | Type | Owner --------+---------------------+----------+----------- public | harmonica_migration | table | developer public | task | table | developer public | task_id_seq | sequence | developer (3 rows) |
harmonica_migration
table contains the executed migration versions. task
table is what we wanted to create and task_id_seq
sequence is the sequence for task
table, which is created automatically.
1 2 3 4 5 6 7 8 9 |
demo=# d task Table "public.task" Column | Type | Collation | Nullable | Default ------------+-----------------------------+-----------+----------+---------------------------------- id | integer | | not null | nextval('task_id_seq'::regclass) name | character varying(200) | | not null | created_at | timestamp without time zone | | not null | Indexes: "task_pkey" PRIMARY KEY, btree (id) |
task
table has id
column, which is added automatically, and it uses the sequence.
This article continues to Spring Boot with DB Migration (2 of 2).