![JAR search and dependency download from the Maven repository](/logo.png)
org.ryoframework.persitence.evolutions.LiquibaseRunner.kt Maven / Gradle / Ivy
The newest version!
package org.ryoframework.persitence.evolutions
import liquibase.Liquibase
import liquibase.change.AddColumnConfig
import liquibase.change.Change
import liquibase.change.ColumnConfig
import liquibase.change.ConstraintsConfig
import liquibase.change.core.*
import liquibase.changelog.ChangeSet
import liquibase.changelog.DatabaseChangeLog
import liquibase.database.DatabaseFactory
import liquibase.database.jvm.JdbcConnection
import liquibase.resource.ClassLoaderResourceAccessor
import liquibase.statement.DatabaseFunction
import liquibase.structure.core.Column
import java.sql.Connection
import java.util.*
object LiquibaseRunner {
fun run(evolution: DbEvolution, connection: Connection) {
val dbConnection = JdbcConnection(connection)
val database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(dbConnection)
val changeLog = DatabaseChangeLog()
val lq = Liquibase(changeLog, ClassLoaderResourceAccessor(), database)
evolution.changeSets.forEach {
addChangeSet(changeLog, it)
}
if (evolution.changeSets.isEmpty()) {
throw Error("NO_MIGRATIONS_FOUND")
}
database.connection = dbConnection
lq.update("")
}
private fun addChangeSet(dbCL: DatabaseChangeLog, cs: DbEvolution.ChangeSet) {
val changeSet = ChangeSet("${cs.timestamp}", cs.author, false, false, "",
cs.contextList, cs.dbmsList, true, dbCL)
cs.changes.flatMap { change ->
when (change) {
is DbEvolution.CreateTable -> createChanges(change)
is DbEvolution.AlterTable -> createChanges(change)
else -> TODO()
}
}.forEach {
changeSet.addChange(it)
}
dbCL.addChangeSet(changeSet)
}
private fun createChanges(def: DbEvolution.CreateTable): List {
return createChanges(def, CreateTableChange())
}
// --------------------------------------------------------------------------------------------------------------
private fun createChanges(def: DbEvolution.AlterTable) = createChanges(def as DbEvolution.TableChange)
private fun createChanges(def: DbEvolution.TableChange, createTableChange: CreateTableChange? = null): List {
val table = def.table
val changes = ChangesHolder()
if (createTableChange != null) {
createTableChange.tableName = table
changes.tables.add(createTableChange)
}
if (def is DbEvolution.AlterTable) {
def.dropColumns.forEach { column ->
changes.columns.add(DropColumnChange().apply {
this.tableName = table
this.columnName = column
})
}
def.renameColumns.forEach { column ->
changes.columns.add(RenameColumnChange().apply {
this.tableName = table
this.newColumnName = column.second
this.oldColumnName = column.first
})
}
}
def.columns.forEach {
val columnName = it.name
if (it.primaryKey) {
val column = ColumnConfig.fromName(columnName).setAutoIncrement(it.autoIncrement).apply {
type = it.type
constraints = ConstraintsConfig().apply {
isPrimaryKey = true
isNullable = false
}
}
column.remarks = "PRIMARY_KEY"
if (createTableChange != null) {
createTableChange.addColumn(column)
} else {
changes.columns.add(AddColumnChange().apply {
this.tableName = table
this.addColumn(AddColumnConfig(Column(column)))
})
}
} else if (it.foreignKey != null) {
createColumnChange(changes, table, columnName, it.type, it.nullable, true, it.unique, null, createTableChange, "FOREIGN_KEY_COLUMN")
changes.indices.add(createForeignKeyChange(table, columnName, it.foreignKey!!))
} else {
createColumnChange(changes, table, columnName, it.type, it.nullable, it.indexed, it.unique, it.defaultValue, createTableChange)
}
}
def.uniqueConstraints.forEach {
// createUniqueIndexChange(table, it.split(","))
changes.indices.add(createUniqueIndexChange(table, it.split(",")))
}
changes.raw.addAll(def.rawSql.map {
RawSQLChange(it).apply {
this.isSplitStatements = false
}
})
return changes.compute()
}
private fun createColumnChange(changes: ChangesHolder, table: String, columnName: String, type: String, nullable: Boolean, indexed: Boolean, unique: Boolean = false, default: Any? = null, createTableChange: CreateTableChange? = null, remarks: String? = null) {
val column = createColumnChange(columnName, type, nullable, default, unique)
if (createTableChange != null) {
createTableChange.addColumn(column)
} else {
changes.columns.add(AddColumnChange().apply {
this.tableName = table
this.addColumn(AddColumnConfig(Column(column)).apply {
this.constraints = column.constraints
this.defaultValue = column.defaultValue
this.defaultValueDate = column.defaultValueDate
this.defaultValueBoolean = column.defaultValueBoolean
this.defaultValueComputed = column.defaultValueComputed
this.defaultValueNumeric = column.defaultValueNumeric
})
})
}
if (unique) changes.indices.add(createUniqueIndexChange(table, listOf(columnName)))
when {
type.contains("geometry", true) -> changes.indices.add(createIndexChange(table, columnName, "GIST"))
// type.contains(Regex("hash|json")) -> changes.indices.add(createIndexChange(table, columnName, "GIN"))
indexed -> changes.indices.add(createIndexChange(table, columnName))
}
column.remarks = remarks
}
private fun createColumnChange(name: String, type: String, nullable: Boolean = true, defaultValue: Any? = null, unique: Boolean = false): ColumnConfig {
val c = ColumnConfig.fromName(name)
c.type = type
c.constraints = ConstraintsConfig().apply {
isNullable = nullable
isUnique = unique
}
if (defaultValue != null) {
if (defaultValue is Date) {
c.defaultValueDate = defaultValue
} else if (defaultValue is Number) {
c.defaultValueNumeric = defaultValue
} else if (defaultValue is Boolean) {
c.defaultValueBoolean = defaultValue
} else if (defaultValue is String) {
if (defaultValue.contains("(")) {
c.defaultValueComputed = DatabaseFunction(defaultValue.trim())
} else {
c.defaultValue = defaultValue
}
} else {
TODO("")
}
}
return c
}
private fun createIndexChange(table: String, column: String, method: String? = null): Change {
return if (method == null) {
CreateIndexChange().apply {
this.indexName = "idx_${table}_$column"
this.tableName = table
this.addColumn(AddColumnConfig(Column.fromName(column)))
}
} else {
RawSQLChange("CREATE INDEX idx_${table}_$column ON $table USING $method ($column)")
}
}
private fun createForeignKeyChange(baseTable: String, baseColumn: String, target: DbEvolution.ForeignKey): AddForeignKeyConstraintChange {
val change = AddForeignKeyConstraintChange()
change.baseTableName = baseTable
change.baseColumnNames = baseColumn
change.constraintName = "fk_${baseTable}_${baseColumn}_${target.table}_${target.column}"
change.referencedTableName = target.table
change.referencedColumnNames = target.column
change.onDelete = target.onDelete
change.onUpdate = target.onUpdate
return change
}
private fun createUniqueIndexChange(table: String, columns: Collection): Change {
return CreateIndexChange().apply {
isUnique = true
this.indexName = "idx_u_${table}_${columns.joinToString("_")}"
this.tableName = table
columns.forEach {
this.addColumn(AddColumnConfig(Column.fromName(it)))
}
}
}
private class ChangesHolder {
val tables = arrayListOf()
val columns = arrayListOf()
val indices = arrayListOf()
val raw = arrayListOf()
fun compute() = tables.plus(columns).plus(indices).plus(raw)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy