All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ryoframework.persitence.evolutions.DbEvolution.kt Maven / Gradle / Ivy

package org.ryoframework.persitence.evolutions;

import java.sql.Connection
import java.util.function.Consumer
import javax.sql.DataSource

class DbEvolution private constructor(val version: Int) {

    val changeSets = arrayListOf()

    companion object {

        @JvmStatic
        @JvmOverloads
        fun newInstance(version: Int = 1) = DbEvolution(version)

    }

    fun apply(connection: Connection) = LiquibaseRunner.run(this, connection)

    fun apply(ds: DataSource) = LiquibaseRunner.run(this, ds.connection)

    // ================================================================================================================

    fun changeSet(timestamp: Int, author: String, callback: Consumer) {
        val cs = ChangeSet(timestamp, author)
        changeSets.add(cs)
        callback.accept(cs)
    }

    fun changeSet(timestamp: Int, author: String, callback: ChangeSet.() -> Unit) {
        val cs = ChangeSet(timestamp, author)
        changeSets.add(cs)
        callback(cs)
    }

    inner class ChangeSet(val timestamp: Int, val author: String) {

        var contextList: String = ""
        var dbmsList: String = ""
        val changes = arrayListOf()

        fun createTable(name: String, callback: Consumer) {
            val change = CreateTable(name)
            changes.add(change)
            callback.accept(change)
        }

        fun createTable(name: String, callback: CreateTable.() -> Unit) {
            val change = CreateTable(name)
            changes.add(change)
            callback(change)
        }

        fun alterTable(name: String, callback: Consumer) {
            val change = AlterTable(name)
            changes.add(change)
            callback.accept(change)
        }

        fun alterTable(name: String, callback: AlterTable.() -> Unit) {
            val change = AlterTable(name)
            changes.add(change)
            callback(change)
        }

    }

    interface Change

    abstract class TableChange(val table: String) : Change {

        var schemaName: String? = null
        val columns = arrayListOf()
        val rawSql = hashSetOf()
        val uniqueConstraints = arrayListOf()

        fun col(name: String): Column {
            val parts = name.replace("!", "").split(":")
            var type = "varchar"
            val lName = parts[0]
            val nullable = !name.endsWith("!")
            if (parts.size == 2) {
                type = parts[1]
            }
            val col = Column(lName, type, nullable)
            columns.add(col)
            return col
        }

        fun sql(query : String) {
            rawSql.add(query)
        }

        /**
         * Rajouter les [columns] à la liste des contraintes d'unicité
         * @param columns Liste des colonnes séparées par une virgule
         */
        fun unique(columns: String) {
            uniqueConstraints.add(columns)
        }

    }

    inner class AlterTable(table: String) : TableChange(table) {

        val dropColumns = hashSetOf()
        val renameColumns = hashSetOf>()

        fun dropColumn(name: String) {
            dropColumns.add(name.trim())
        }

        fun renameColumn(from: String, to: String) {
            renameColumns.add(from to to)
        }

    }

    inner class CreateTable(table: String) : TableChange(table) {

    }

    class Column(internal val name: String, internal val type: String, internal var nullable: Boolean = true) {

        internal var indexed: Boolean = false
        internal var defaultValue: Any? = null
        internal var autoIncrement: Boolean = false
        internal var primaryKey: Boolean = false
        internal var unique: Boolean = false
        internal var foreignKey: ForeignKey? = null

        init {

            if (!name.matches(Regex("[a-zA-Z0-9_]+"))) throw Error("Invalid column name: $name")

        }

        fun autoIncrement(): Column {
            this.autoIncrement = true
            return this
        }

        fun defaultValue(value: Any): Column {
            this.defaultValue = value
            return this
        }

        @JvmOverloads
        fun pk(autoIncrement: Boolean = false) = primaryKey(autoIncrement)

        fun primaryKey(autoIncrement: Boolean = false): Column {
            this.primaryKey = true
            this.autoIncrement = autoIncrement
            return this
        }

        fun fk(target: String): ForeignKey {
            this.foreignKey = ForeignKey.create(target)
            return this.foreignKey!!
        }

        fun notNull(): Column {
            nullable = false
            return this
        }

        fun unique(): Column {
            unique = true
            return this
        }

        fun indexed(): Column {
            indexed = true
            return this
        }

    }

    class ForeignKey(val table: String, val column: String) {

        internal var onDelete: String = "RESTRICT"
        internal var onUpdate: String = "RESTRICT"

        fun onDeleteCascade() {
            this.onDelete = "CASCADE"
        }

        fun onDeleteSetNull() {
            this.onDelete = "SET NULL"
        }

        fun onDeleteSetDefault() {
            this.onDelete = "SET DEFAULT"
        }

        fun onDeleteRestrict() {
            this.onDelete = "RESTRICT"
        }

        fun onUpdateCascade() {
            this.onDelete = "CASCADE"
        }

        fun onUpdateSetNull() {
            this.onDelete = "SET NULL"
        }

        fun onUpdateSetDefault() {
            this.onDelete = "SET DEFAULT"
        }

        fun onUpdateRestrict() {
            this.onDelete = "RESTRICT"
        }

        companion object {

            fun create(target: String): ForeignKey {
                val parts = target.split(".")
                return ForeignKey(parts[0], parts[1])
            }

        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy