From 81e55c446cac45fe4ace39d46e23b6f5a34cba30 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Fri, 11 Oct 2019 17:21:33 -0500 Subject: [PATCH 1/3] Fix List Query 0.5 branch --- .../com/vladsch/kotlin/jdbc/ModelProperties.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt b/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt index 29aba77..eeb954a 100644 --- a/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt +++ b/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt @@ -615,8 +615,18 @@ class ModelProperties(val session: Session, val tableName: String, val dbCase fun listQuery(whereClause: String, params: Map, alias: String? = null): SqlQuery { val sb = StringBuilder() - appendSelectSql(sb, alias) - appendSelectSql(sb).append(whereClause, alias) + val aliasedWhereClause = if(!alias.isNullOrEmpty()) { + whereClause.split(" ").joinToString(" ") { + if (properties.containsKey(it) || propertyDefaults.containsKey(it)) { + "$alias.$it" + } else { + it + } + } + } else { + whereClause + } + appendSelectSql(sb, alias).append(" WHERE ", aliasedWhereClause) return sqlQuery(sb.toString(), params) } } From fc4b633b19b3e81426951e9d0caff07106526eff Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Fri, 11 Oct 2019 17:39:41 -0500 Subject: [PATCH 2/3] Adds batch and bulk insert operations Tests have not yet been made --- .../kotlin/com/vladsch/kotlin/jdbc/Model.kt | 4 + .../vladsch/kotlin/jdbc/ModelProperties.kt | 159 ++++++++++++++---- .../kotlin/jdbc/ModelPropertyProviderBase.kt | 10 +- 3 files changed, 132 insertions(+), 41 deletions(-) diff --git a/src/main/kotlin/com/vladsch/kotlin/jdbc/Model.kt b/src/main/kotlin/com/vladsch/kotlin/jdbc/Model.kt index 647cdc6..4efd092 100644 --- a/src/main/kotlin/com/vladsch/kotlin/jdbc/Model.kt +++ b/src/main/kotlin/com/vladsch/kotlin/jdbc/Model.kt @@ -42,6 +42,10 @@ abstract class Model, D>(session: Session?, sqlTable: String, db return _db.appendSelectSql(this) } + fun bulkInsert(items: List) = _db.bulkInsert(items) + fun batchInsert(items: List) = _db.batchInsert(items) + fun bulkInsertIgnoreKeys(items: List) = _db.bulkInsertIgnoreKeys(items) + fun batchInsertIgnoreKeys(items: List) = _db.batchInsertIgnoreKeys(items) fun insert() = _db.insert() fun insertIgnoreKeys() = _db.insertIgnoreKeys() fun select() = _db.select() diff --git a/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt b/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt index eeb954a..cd7df60 100644 --- a/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt +++ b/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt @@ -11,7 +11,7 @@ import kotlin.reflect.KMutableProperty import kotlin.reflect.KProperty import kotlin.reflect.KVisibility -class ModelProperties(val session: Session, val tableName: String, val dbCase: Boolean, val allowSetAuto: Boolean = false, quote: String? = null) : InternalModelPropertyProvider { +class ModelProperties>(val session: Session, val tableName: String, val dbCase: Boolean, val allowSetAuto: Boolean = false, quote: String? = null) : InternalModelPropertyProvider { private val properties = HashMap() private val kProperties = ArrayList>() private val propertyTypes = HashMap() @@ -325,43 +325,12 @@ class ModelProperties(val session: Session, val tableName: String, val dbCase fun sqlInsertQuery(): SqlQuery { val sb = StringBuilder() - val sbValues = StringBuilder() - val params = ArrayList() - var sep = "" - - sb.append("INSERT INTO ") - appendQuoted(sb, tableName).append(" (") - - for (prop in kProperties) { - val propType = propertyTypes[prop.name] ?: PropertyType.PROPERTY - if (!propType.isAuto) { - if (properties.containsKey(prop.name) || propertyDefaults.containsKey(prop.name)) { - val propValue = properties[prop.name] - // skip null properties which have defaults (null means use default) - if (propValue != null || !propType.isDefault) { - val columnName = columnNames[prop.name] ?: prop.name - sb.append(sep) - appendQuoted(sb, columnName) - sbValues.append(sep).append("?") - sep = ", " - params.add(propValue) - } else if (propertyDefaults.containsKey(prop.name)) { - val columnName = columnNames[prop.name] ?: prop.name - sb.append(sep) - appendQuoted(sb, columnName) - sbValues.append(sep).append("?") - sep = ", " - params.add(propertyDefaults[prop.name]) - } - } else if (!prop.returnType.isMarkedNullable && !propType.isDefault) { - throw IllegalStateException("$tableName.${prop.name} property is not nullable nor default and not defined in ${this}") - } - } - } + sb.append(getBaseInsertString()) + sb.append(" VALUES ") + sb.append(getInsertQuestionMarks()) - sb.append(") VALUES (").append(sbValues).append(")") - return sqlQuery(sb.toString(), params) + return sqlQuery(sb.toString(), generateInsertString()) } @Suppress("MemberVisibilityCanBePrivate") @@ -530,6 +499,124 @@ class ModelProperties(val session: Session, val tableName: String, val dbCase session.update(sqlInsertQuery()) } + fun bulkInsert(items: List) { + bulkInsertIgnoreKeys(items) + items.forEach { it._db.select() } + } + + fun bulkInsertIgnoreKeys(items: List) { + val sb = StringBuilder() + + sb.append(getBaseInsertString()) + sb.append(" VALUES ") + + val s = items.map { it._db.generateInsertString() } + + val appStr = ArrayList().apply { repeat(s.size) { add(getInsertQuestionMarks()) } }.joinToString(separator = ",") + val allItems = s.reduce { acc, arrayList -> ArrayList(acc + arrayList) } + + session.execute(sqlQuery(sb.append(appStr).toString(), allItems)) + } + + fun batchInsert(items: List) { + batchInsertIgnoreKeys(items) + items.forEach { it._db.select() } + } + + fun batchInsertIgnoreKeys(items: List) { + val sb = StringBuilder() + + sb.append(getBaseInsertString()) + sb.append(" VALUES ") + sb.append(getInsertQuestionMarks()) + + val stmt = session.prepare(sqlQuery(sb.toString())) + + items.map { it._db.generateInsertString() }.forEach { + it.forEachIndexed { index, value -> + stmt.setTypedParam(index + 1, value.param()) + } + stmt.addBatch() + } + } + + internal fun generateInsertString(): ArrayList { + val params = ArrayList() + + for (prop in kProperties) { + val propType = propertyTypes[prop.name] ?: PropertyType.PROPERTY + + if (!propType.isAuto) { + if (properties.containsKey(prop.name) || propertyDefaults.containsKey(prop.name)) { + val propValue = properties[prop.name] + // skip null properties which have defaults (null means use default) + if (propValue != null || !propType.isDefault) { + params.add(propValue) + } else if (propertyDefaults.containsKey(prop.name)) { + params.add(propertyDefaults[prop.name]) + } + } else if (!prop.returnType.isMarkedNullable && !propType.isDefault) { + throw IllegalStateException("$tableName.${prop.name} property is not nullable nor default and not defined in ${this}") + } + } + } + return params + } + + internal fun getInsertQuestionMarks(): String { + val sbValues = StringBuilder().append('(') + var sep = "" + + for (prop in kProperties) { + val propType = propertyTypes[prop.name] ?: PropertyType.PROPERTY + + if (!propType.isAuto) { + if (properties.containsKey(prop.name) || propertyDefaults.containsKey(prop.name)) { + sbValues.append(sep).append("?") + sep = ", " + } else if (!prop.returnType.isMarkedNullable && !propType.isDefault) { + throw IllegalStateException("$tableName.${prop.name} property is not nullable nor default and not defined in ${this}") + } + } + } + + return sbValues.append(')').toString() + } + + internal fun getBaseInsertString(): String { + val sb = StringBuilder() + var sep = "" + + sb.append("INSERT INTO ") + appendQuoted(sb, tableName).append(" (") + + for (prop in kProperties) { + val propType = propertyTypes[prop.name] ?: PropertyType.PROPERTY + + if (!propType.isAuto) { + if (properties.containsKey(prop.name) || propertyDefaults.containsKey(prop.name)) { + val propValue = properties[prop.name] + // skip null properties which have defaults (null means use default) + if (propValue != null || !propType.isDefault) { + val columnName = columnNames[prop.name] ?: prop.name + sb.append(sep) + appendQuoted(sb, columnName) + sep = ", " + } else if (propertyDefaults.containsKey(prop.name)) { + val columnName = columnNames[prop.name] ?: prop.name + sb.append(sep) + appendQuoted(sb, columnName) + sep = ", " + } + } else if (!prop.returnType.isMarkedNullable && !propType.isDefault) { + throw IllegalStateException("$tableName.${prop.name} property is not nullable nor default and not defined in ${this}") + } + } + } + + return sb.append(')').toString() + } + fun select() { session.first(sqlSelectQuery()) { load(it) diff --git a/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelPropertyProviderBase.kt b/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelPropertyProviderBase.kt index 1a5d42e..7fad656 100644 --- a/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelPropertyProviderBase.kt +++ b/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelPropertyProviderBase.kt @@ -2,7 +2,7 @@ package com.vladsch.kotlin.jdbc import kotlin.reflect.KProperty -open class ModelPropertyProviderBase(val provider: ModelProperties, val propType: PropertyType, override val columnName: String?, override val defaultValue:Any? = Unit) : InternalModelPropertyProvider { +open class ModelPropertyProviderBase>(val provider: ModelProperties, val propType: PropertyType, override val columnName: String?, override val defaultValue:Any? = Unit) : InternalModelPropertyProvider { final override fun provideDelegate(thisRef: T, prop: KProperty<*>): ModelProperties { return provider.registerProp(prop, propType, columnName, defaultValue) } @@ -44,7 +44,7 @@ open class ModelPropertyProviderBase(val provider: ModelProperties, val pr } } -class ModelPropertyProviderAutoKey(provider: ModelProperties, columnName: String?) : ModelPropertyProviderBase(provider, PropertyType.AUTO_KEY, columnName) { +class ModelPropertyProviderAutoKey>(provider: ModelProperties, columnName: String?) : ModelPropertyProviderBase(provider, PropertyType.AUTO_KEY, columnName) { override val key: ModelPropertyProvider get() = this @@ -72,7 +72,7 @@ class ModelPropertyProviderAutoKey(provider: ModelProperties, columnName: } } -class ModelPropertyProviderKey(provider: ModelProperties, columnName: String?) : ModelPropertyProviderBase(provider, PropertyType.KEY, columnName) { +class ModelPropertyProviderKey>(provider: ModelProperties, columnName: String?) : ModelPropertyProviderBase(provider, PropertyType.KEY, columnName) { override val key: ModelPropertyProvider get() = this @@ -100,7 +100,7 @@ class ModelPropertyProviderKey(provider: ModelProperties, columnName: Stri } } -class ModelPropertyProviderAuto(provider: ModelProperties, columnName: String?) : ModelPropertyProviderBase(provider, PropertyType.AUTO, columnName) { +class ModelPropertyProviderAuto>(provider: ModelProperties, columnName: String?) : ModelPropertyProviderBase(provider, PropertyType.AUTO, columnName) { override val key: ModelPropertyProvider get() = provider.autoKey @@ -128,7 +128,7 @@ class ModelPropertyProviderAuto(provider: ModelProperties, columnName: Str } } -class ModelPropertyProviderDefault(provider: ModelProperties, columnName: String?, value: Any?) : ModelPropertyProviderBase(provider, PropertyType.DEFAULT, columnName, value) { +class ModelPropertyProviderDefault>(provider: ModelProperties, columnName: String?, value: Any?) : ModelPropertyProviderBase(provider, PropertyType.DEFAULT, columnName, value) { override val key: ModelPropertyProvider get() = provider.autoKey From 3a0e11338bd875aa03b52f05336ede7c26283ec2 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Sat, 12 Oct 2019 15:54:24 -0500 Subject: [PATCH 3/3] Acutally add items on batch insert --- src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt b/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt index cd7df60..a011324 100644 --- a/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt +++ b/src/main/kotlin/com/vladsch/kotlin/jdbc/ModelProperties.kt @@ -538,6 +538,8 @@ class ModelProperties>(val session: Session, val tableName: Stri } stmt.addBatch() } + + stmt.executeBatch() } internal fun generateInsertString(): ArrayList {