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])
}
}
}
}