Skip to content

docs: EXPOSED-756 Add documentation for SQL migration options #2471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions documentation-website/Writerside/hi.tree
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<toc-element topic="SQL-Functions.md"/>
<toc-element topic="Working-with-Schema.topic"/>
</toc-element>
<toc-element topic="Migrations.md"/>
<toc-element toc-title="Transactions">
<toc-element topic="Transactions.md"/>
<toc-element topic="Working-with-SQL-Strings.md"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Exposed Migrations

A Gradle application that shows how to generate a migration script using Exposed and apply a migration using Flyway.
The files are referenced in the [Migrations](../../topics/Migrations.md) topic.

## Build

To build the application, in a terminal window navigate to the `snippets` folder and run the following command:

```shell
./gradlew :exposed-migrations:build
```

## Generate a migration script

To run the application, in a terminal window navigate to the `snippets` folder and run the following command:

```shell
./gradlew :exposed-migrations:generateMigrationScript
```

## Run

To run the application, in a terminal window navigate to the `snippets` folder and run the following command:

```shell
./gradlew :exposed-migrations:run
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
plugins {
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
alias(libs.plugins.jvm)

// Apply the application plugin to add support for building a CLI application in Java.
application
}

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}

dependencies {
// Use the Kotlin JUnit 5 integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")

// Use the JUnit 5 integration.
testImplementation(libs.junit.jupiter.engine)

testRuntimeOnly("org.junit.platform:junit-platform-launcher")

// This dependency is used by the application.
implementation(libs.guava)
implementation(libs.exposed.core)
implementation(libs.exposed.jdbc)
implementation(libs.exposed.kotlin.datetime)
implementation(libs.exposed.migration)
implementation(libs.h2)
implementation(libs.flyway)
}

// Apply a specific Java toolchain to ease working on different environments.
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

application {
// Define the main class for the application.
mainClass = "org.example.AppKt"
}

tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}

tasks.register<JavaExec>("generateMigrationScript") {
group = "application"
description = "Generate a migration script in the path exposed-migrations/src/main/kotlin/org/example/migrations"
classpath = sourceSets.main.get().runtimeClasspath
mainClass = "org.example.GenerateMigrationScriptKt"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.example

import MigrationUtils
import org.example.tables.UsersTable
import org.flywaydb.core.Flyway
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.vendors.currentDialect
import java.util.*

/*
Important: The contents of this file are referenced by line number in `Migrations.md`.
If you add, remove, or modify any lines, ensure you update the corresponding
line numbers in the `code-block` element of the referenced file.
*/

const val URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"
const val USER = "root"
const val PASSWORD = ""
const val MIGRATIONS_DIRECTORY = "src/main/kotlin/org/example/migrations" // Location of migration scripts

fun main() {
val h2db = Database.connect(
url = URL,
driver = "org.h2.Driver",
user = USER,
password = PASSWORD
)

val flyway = Flyway.configure()
.dataSource(URL, USER, PASSWORD)
.locations("filesystem:$MIGRATIONS_DIRECTORY")
.baselineOnMigrate(true) // Used when migrating an existing database for the first time
.load()

simulateExistingDatabase(h2db)

transaction(h2db) {
println("*** Before migration ***")
println("Primary key: ${currentDialect.existingPrimaryKeys(UsersTable)[UsersTable]}")

// Generate a migration script
generateMigrationScript()
}

transaction(h2db) {
// Generate SQL statements required to align the database schema
// against the current table definitions
val statements = MigrationUtils.statementsRequiredForDatabaseMigration(
UsersTable
)
println(statements)

// Disable logging
MigrationUtils.statementsRequiredForDatabaseMigration(
UsersTable,
withLogs = false
)

// Identify columns that are no longer present in the current table definitions
// and return the SQL statements to remove them
val dropStatements = MigrationUtils.dropUnmappedColumnsStatements(
UsersTable
)
println(dropStatements)

// SchemaUtils methods
val missingColStatements = SchemaUtils.addMissingColumnsStatements(
UsersTable
)
println(missingColStatements)

// This can be commented out to review the generated migration script before applying a migration
flyway.migrate()
}

transaction(h2db) {
println("*** After migration ***")
println("Primary key: ${currentDialect.existingPrimaryKeys(UsersTable)[UsersTable]}")

UsersTable.insert {
it[id] = UUID.randomUUID()
it[email] = "root2@root.com"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@file:OptIn(ExperimentalDatabaseMigrationApi::class)

package org.example

import org.example.tables.UsersTable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.ExperimentalDatabaseMigrationApi
import org.jetbrains.exposed.sql.transactions.transaction

val h2db = Database.connect(
url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
driver = "org.h2.Driver",
user = "root",
password = ""
)

fun main() {
simulateExistingDatabase(h2db)

transaction(h2db) {
generateMigrationScript()
}
}

fun simulateExistingDatabase(database: Database) {
transaction(database) {
exec("DROP TABLE IF EXISTS USERS")
exec("CREATE TABLE IF NOT EXISTS USERS (ID UUID NOT NULL, EMAIL VARCHAR(320) NOT NULL)")
exec("INSERT INTO USERS (EMAIL, ID) VALUES ('root1@root.com', '05fb3246-9387-4d04-a27f-fa0107c33883')")
}
}

fun generateMigrationScript() {
// Generate a migration script in the specified path
MigrationUtils.generateMigrationScript(
UsersTable,
scriptDirectory = MIGRATIONS_DIRECTORY,
scriptName = "V2__Add_primary_key",
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.example.tables

import org.jetbrains.exposed.sql.Table

const val EMAIL_LIMIT = 320

object UsersTable : Table("Users") {
val id = uuid("id")
val email = varchar("email", EMAIL_LIMIT)

override val primaryKey = PrimaryKey(id)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ postgres = "42.7.3"
slf4j = "2.0.16"
jackson = "2.18.+"
kotlin = "2.1.0"
flyway = "10.15.0"

[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
Expand All @@ -20,11 +21,13 @@ exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "e
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
exposed-kotlin-datetime = { module = "org.jetbrains.exposed:exposed-kotlin-datetime", version.ref = "exposed" }
exposed-json = { module= "org.jetbrains.exposed:exposed-json", version.ref = "exposed" }
exposed-migration = { module = "org.jetbrains.exposed:exposed-migration", version.ref = "exposed" }
h2 = { module = "com.h2database:h2", version.ref = "h2" }
mysql = { module = "mysql:mysql-connector-java", version.ref = "mysql"}
postgresql = { module = "org.postgresql:postgresql", version.ref = "postgres"}
slf4j = { module = "org.slf4j:slf4j-nop", version.ref = "slf4j" }
jackson-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
flyway = { module = "org.flywaydb:flyway-core", version.ref = "flyway" }

[plugins]
jvm = { id = "org.jetbrains.kotlin.jvm", version = "2.1.0" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ include("exposed-modules-kotlin-gradle")
include("exposed-modules-groovy-gradle")
include("exposed-sql-functions")
include("exposed-transactions")
include("exposed-migrations")
Loading
Loading