changes in insert/update exposed methods and rewriting of read keyvalue(s) exposed repos to be extending abstract key value(s) repos

This commit is contained in:
InsanusMokrassar 2022-10-11 12:55:25 +06:00
parent 1107b7f4ef
commit 1257492f85
5 changed files with 89 additions and 108 deletions

View File

@ -2,6 +2,15 @@
## 0.13.0 ## 0.13.0
* `Repos`:
* `Exposed`:
* `AbstractExposedWriteCRUDRepo` got two new methods: `update` with `it` as `UpdateBuilder<Int>` and `createAndInsertId`
* Old `update` method has been deprecated and not recommended to override anymore in realizations
* Old `insert` method now is `open` instead of `abstract` and can be omitted
* `AbstractExposedKeyValueRepo` got two new methods: `update` with `it` as `UpdateBuilder<Int>` and `insertKey`
* Old `update` method has been deprecated and not recommended to override anymore
* Old `insert` method now is `open` instead of `abstract` and can be omitted in realizations
## 0.12.17 ## 0.12.17
* `Versions`: * `Versions`:

View File

@ -4,9 +4,9 @@ import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteCRUDRepo import dev.inmo.micro_utils.repos.WriteCRUDRepo
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement import org.jetbrains.exposed.sql.statements.*
import org.jetbrains.exposed.sql.statements.UpdateStatement
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import java.util.Objects
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>( abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 0, flowsChannelsSize: Int = 0,
@ -27,10 +27,31 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
protected abstract fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType protected abstract fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>) protected abstract fun update(id: IdType, value: InputValueType, it: UpdateBuilder<Int>)
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement) protected abstract fun createAndInsertId(value: InputValueType, it: InsertStatement<Number>): IdType
protected open fun insert(value: InputValueType, it: InsertStatement<Number>) {
val id = createAndInsertId(value, it)
update(id, value, it as UpdateBuilder<Int>)
}
@Deprecated(
"Replace its \"it\" parameter type with \"UpdateBuilder<Int>\" to actualize method signature. Method with current signature will be removed soon and do not recommended to override anymore"
)
protected open fun update(id: IdType, value: InputValueType, it: UpdateStatement) = update(
id,
value,
it as UpdateBuilder<Int>
)
protected open suspend fun onBeforeCreate(value: List<InputValueType>) {} protected open suspend fun onBeforeCreate(value: List<InputValueType>) {}
/**
* Use this method to do the something with [values]. You may change and output values in that list and return
* changed list of pairs
*/
protected open suspend fun onAfterCreate(
values: List<Pair<InputValueType, ObjectType>>
): List<ObjectType> = values.map { it.second }
private fun createWithoutNotification(value: InputValueType): ObjectType { private fun createWithoutNotification(value: InputValueType): ObjectType {
return transaction(database) { return transaction(database) {
insert { insert(value, it) }.asObject(value) insert { insert(value, it) }.asObject(value)
@ -40,13 +61,18 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
override suspend fun create(values: List<InputValueType>): List<ObjectType> { override suspend fun create(values: List<InputValueType>): List<ObjectType> {
onBeforeCreate(values) onBeforeCreate(values)
return transaction(db = database) { return transaction(db = database) {
values.map { value -> createWithoutNotification(value) } values.map { value -> value to createWithoutNotification(value) }
}.let {
onAfterCreate(it)
}.onEach { }.onEach {
_newObjectsFlow.emit(it) _newObjectsFlow.emit(it)
} }
} }
protected open suspend fun onBeforeUpdate(value: List<UpdatedValuePair<IdType, InputValueType>>) {} protected open suspend fun onBeforeUpdate(value: List<UpdatedValuePair<IdType, InputValueType>>) {}
protected open suspend fun onAfterUpdate(
value: List<UpdatedValuePair<InputValueType, ObjectType>>
): List<ObjectType> = value.map { it.second }
private fun updateWithoutNotification(id: IdType, value: InputValueType): ObjectType? { private fun updateWithoutNotification(id: IdType, value: InputValueType): ObjectType? {
return transaction(db = database) { return transaction(db = database) {
update( update(
@ -54,7 +80,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
selectById(this, id) selectById(this, id)
} }
) { ) {
update(id, value, it) update(id, value, it as UpdateBuilder<Int>)
} }
}.let { }.let {
if (it > 0) { if (it > 0) {
@ -71,7 +97,9 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
override suspend fun update(id: IdType, value: InputValueType): ObjectType? { override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
onBeforeUpdate(listOf(id to value)) onBeforeUpdate(listOf(id to value))
return updateWithoutNotification(id, value).also { return updateWithoutNotification(id, value).let {
onAfterUpdate(listOf(value to (it ?: return@let emptyList())))
}.firstOrNull().also {
if (it != null) { if (it != null) {
_updatedObjectsFlow.emit(it) _updatedObjectsFlow.emit(it)
} }
@ -81,9 +109,11 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
onBeforeUpdate(values) onBeforeUpdate(values)
return ( return (
transaction(db = database) { transaction(db = database) {
values.map { (id, value) -> updateWithoutNotification(id, value) } values.mapNotNull { (id, value) -> value to (updateWithoutNotification(id, value) ?: return@mapNotNull null) }
}.filterNotNull() }
).onEach { ).let {
onAfterUpdate(it)
}.onEach {
_updatedObjectsFlow.emit(it) _updatedObjectsFlow.emit(it)
} }
} }

View File

@ -3,8 +3,7 @@ package dev.inmo.micro_utils.repos.exposed.keyvalue
import dev.inmo.micro_utils.repos.KeyValueRepo import dev.inmo.micro_utils.repos.KeyValueRepo
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement import org.jetbrains.exposed.sql.statements.*
import org.jetbrains.exposed.sql.statements.UpdateStatement
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedKeyValueRepo<Key, Value>( abstract class AbstractExposedKeyValueRepo<Key, Value>(
@ -20,13 +19,27 @@ abstract class AbstractExposedKeyValueRepo<Key, Value>(
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow() override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow() override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
protected abstract fun update(k: Key, v: Value, it: UpdateStatement) protected abstract fun update(k: Key, v: Value, it: UpdateBuilder<Int>)
protected abstract fun insert(k: Key, v: Value, it: InsertStatement<Number>) protected abstract fun insertKey(k: Key, v: Value, it: InsertStatement<Number>)
protected open fun insert(k: Key, v: Value, it: InsertStatement<Number>) {
insertKey(k, v, it)
update(k, v, it as UpdateBuilder<Int>)
}
@Deprecated(
"Replace its \"it\" parameter type with \"UpdateBuilder<Int>\" to actualize method signature. Method with current signature will be removed soon and do not recommended to override anymore"
)
protected open fun update(k: Key, v: Value, it: UpdateStatement) = update(
k,
v,
it as UpdateBuilder<Int>
)
override suspend fun set(toSet: Map<Key, Value>) { override suspend fun set(toSet: Map<Key, Value>) {
transaction(database) { transaction(database) {
toSet.mapNotNull { (k, v) -> toSet.mapNotNull { (k, v) ->
if (update({ selectById(k) }) { update(k, v, it) } > 0) { if (update({ selectById(k) }) { update(k, v, it as UpdateBuilder<Int>) } > 0) {
k to v k to v
} else { } else {
val inserted = insert { val inserted = insert {

View File

@ -5,57 +5,27 @@ import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.* import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.statements.UpdateBuilder
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
open class ExposedReadKeyValueRepo<Key, Value>( open class ExposedReadKeyValueRepo<Key, Value>(
override val database: Database, database: Database,
keyColumnAllocator: ColumnAllocator<Key>, keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>, valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null tableName: String? = null
) : ReadKeyValueRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") { ) : ReadKeyValueRepo<Key, Value>, ExposedRepo, AbstractExposedReadKeyValueRepo<Key, Value>(database, tableName) {
val keyColumn: Column<Key> = keyColumnAllocator()
override val keyColumn: Column<Key> = keyColumnAllocator()
val valueColumn: Column<Value> = valueColumnAllocator() val valueColumn: Column<Value> = valueColumnAllocator()
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn) override val ResultRow.asKey: Key
get() = get(keyColumn)
override val selectByValue: SqlExpressionBuilder.(Value) -> Op<Boolean> = { valueColumn.eq(it) }
override val ResultRow.asObject: Value
get() = get(valueColumn)
override val selectById: SqlExpressionBuilder.(Key) -> Op<Boolean> = { keyColumn.eq(it) }
override val primaryKey: Table.PrimaryKey
get() = PrimaryKey(keyColumn, valueColumn)
init { initTable() } init { initTable() }
override suspend fun get(k: Key): Value? = transaction(database) {
select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn)
}
override suspend fun contains(key: Key): Boolean = transaction(database) {
select { keyColumn.eq(key) }.limit(1).any()
}
override suspend fun count(): Long = transaction(database) { selectAll().count() }
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
selectAll().selectPaginated(
pagination,
keyColumn,
reversed
) {
it[keyColumn]
}
}
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
select { valueColumn.eq(v) }.selectPaginated(
pagination,
keyColumn,
reversed
) {
it[keyColumn]
}
}
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = transaction(database) {
selectAll().selectPaginated(
pagination,
keyColumn,
reversed
) {
it[valueColumn]
}
}
} }

View File

@ -1,10 +1,8 @@
package dev.inmo.micro_utils.repos.exposed.onetomany package dev.inmo.micro_utils.repos.exposed.onetomany
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.* import dev.inmo.micro_utils.repos.exposed.*
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
typealias ExposedReadOneToManyKeyValueRepo<Key, Value> = ExposedReadKeyValuesRepo<Key, Value> typealias ExposedReadOneToManyKeyValueRepo<Key, Value> = ExposedReadKeyValuesRepo<Key, Value>
@ -13,54 +11,15 @@ open class ExposedReadKeyValuesRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>, keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>, valueColumnAllocator: ColumnAllocator<Value>,
tableName: String? = null tableName: String? = null
) : ReadKeyValuesRepo<Key, Value>, ExposedRepo, Table(tableName ?: "") { ) : ReadKeyValuesRepo<Key, Value>, ExposedRepo, AbstractExposedReadKeyValuesRepo<Key, Value>(database, tableName) {
val keyColumn: Column<Key> = keyColumnAllocator() override val keyColumn: Column<Key> = keyColumnAllocator()
override val ResultRow.asKey: Key
get() = get(keyColumn)
override val selectByValue: SqlExpressionBuilder.(Value) -> Op<Boolean> = { valueColumn.eq(it) }
override val ResultRow.asObject: Value
get() = get(valueColumn)
override val selectById: SqlExpressionBuilder.(Key) -> Op<Boolean> = { keyColumn.eq(it) }
val valueColumn: Column<Value> = valueColumnAllocator() val valueColumn: Column<Value> = valueColumnAllocator()
init { initTable() } init { initTable() }
override suspend fun count(k: Key): Long = transaction(database) { select { keyColumn.eq(k) }.count() }
override suspend fun count(): Long = transaction(database) { selectAll().count() }
override suspend fun get(
k: Key,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Value> = transaction(database) {
select { keyColumn.eq(k) }.paginate(pagination, keyColumn, reversed).map { it[valueColumn] }
}.createPaginationResult(
pagination,
count(k)
)
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
selectAll().paginate(pagination, keyColumn, reversed).map { it[keyColumn] }
}.createPaginationResult(
pagination,
count()
)
override suspend fun keys(
v: Value,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = transaction(database) {
select { valueColumn.eq(v) }.let {
it.count() to it.paginate(pagination, keyColumn, reversed).map { it[keyColumn] }
}
}.let { (count, list) ->
list.createPaginationResult(
pagination,
count
)
}
override suspend fun contains(k: Key): Boolean = transaction(database) {
select { keyColumn.eq(k) }.limit(1).any()
}
override suspend fun contains(k: Key, v: Value): Boolean = transaction(database) {
select { keyColumn.eq(k).and(valueColumn.eq(v)) }.limit(1).any()
}
} }