This commit is contained in:
InsanusMokrassar 2020-07-30 19:44:47 +06:00
parent 3baf6cc3c6
commit 43f148bf4b
8 changed files with 267 additions and 0 deletions

View File

@ -20,3 +20,8 @@ fun Query.paginate(with: Pagination, orderBy: Pair<Expression<*>, SortOrder>? =
it
}
}
fun Query.paginate(with: Pagination, orderBy: Expression<*>?, reversed: Boolean = false) = paginate(
with,
orderBy ?.let { it to if (reversed) SortOrder.DESC else SortOrder.ASC }
)

View File

@ -0,0 +1,13 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import kotlinx.coroutines.channels.Channel
abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = Channel.BUFFERED,
databaseName: String = ""
) :
AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize,
databaseName
),
ExposedCRUDRepo<ObjectType, IdType>

View File

@ -0,0 +1,39 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import com.insanusmokrassar.postssystem.exposed.commons.paginate
import com.insanusmokrassar.postssystem.utils.repos.ReadStandardCRUDRepo
import com.insanusmokrassar.postssystem.utils.repos.pagination.*
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedReadCRUDRepo<ObjectType, IdType>(
tableName: String
) :
ReadStandardCRUDRepo<ObjectType, IdType>,
ExposedCRUDRepo<ObjectType, IdType>,
Table(tableName)
{
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> {
return transaction(database) {
selectAll().paginate(pagination).map {
it.asObject
}.createPaginationResult(
pagination,
selectAll().count()
)
}
}
override suspend fun getById(id: IdType): ObjectType? {
return transaction(database) {
select {
selectById(id)
}.limit(1).firstOrNull() ?.asObject
}
}
override suspend fun contains(id: IdType): Boolean = transaction(database) {
select { selectById(id) }.limit(1).any()
}
}

View File

@ -0,0 +1,105 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import com.insanusmokrassar.postssystem.utils.repos.UpdatedValuePair
import com.insanusmokrassar.postssystem.utils.repos.WriteStandardCRUDRepo
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.statements.UpdateStatement
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 64,
databaseName: String = ""
) :
AbstractExposedReadCRUDRepo<ObjectType, IdType>(databaseName),
ExposedCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
{
protected val newObjectsChannel = BroadcastChannel<ObjectType>(flowsChannelsSize)
protected val updateObjectsChannel = BroadcastChannel<ObjectType>(flowsChannelsSize)
protected val deleteObjectsIdsChannel = BroadcastChannel<IdType>(flowsChannelsSize)
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asFlow()
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asFlow()
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asFlow()
abstract val InsertStatement<Number>.asObject: ObjectType
abstract val selectByIds: SqlExpressionBuilder.(Array<out IdType>) -> Op<Boolean>
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
protected open suspend fun onBeforeCreate(vararg value: InputValueType) {}
private fun createWithoutNotification(value: InputValueType): ObjectType {
return transaction(database) {
insert { insert(value, it) }.asObject
}
}
override suspend fun create(vararg values: InputValueType): List<ObjectType> {
onBeforeCreate(*values)
return transaction(database) {
values.map { value -> createWithoutNotification(value) }
}.also {
it.forEach {
newObjectsChannel.send(it)
}
}
}
protected open suspend fun onBeforeUpdate(vararg value: UpdatedValuePair<IdType, InputValueType>) {}
private fun updateWithoutNotification(id: IdType, value: InputValueType): ObjectType? {
return transaction(database) {
update(
{
selectById(this, id)
}
) {
update(id, value, it)
}
}.let {
if (it > 0) {
select {
selectById(this, id)
}.limit(1).firstOrNull() ?.asObject
} else {
null
}
}
}
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
onBeforeUpdate(id to value)
return updateWithoutNotification(id, value).also {
if (it != null) {
updateObjectsChannel.send(it)
}
}
}
override suspend fun update(vararg values: UpdatedValuePair<IdType, InputValueType>): List<ObjectType> {
onBeforeUpdate(*values)
return (
transaction(database) {
values.map { (id, value) -> updateWithoutNotification(id, value) }
}.filter {
it != null
} as List<ObjectType>
).also {
it.forEach {
updateObjectsChannel.send(it)
}
}
}
protected open suspend fun onBeforeDelete(vararg ids: IdType) {}
override suspend fun deleteById(vararg ids: IdType) {
onBeforeDelete(*ids)
transaction(database) {
deleteWhere(null, null) {
selectByIds(ids)
}
}
}
}

View File

@ -0,0 +1,13 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.SqlExpressionBuilder
interface ExposedCRUDRepo<ObjectType, IdType> {
val database: Database
val ResultRow.asObject: ObjectType
val selectById: SqlExpressionBuilder.(IdType) -> Op<Boolean>
}

View File

@ -0,0 +1,10 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.transactions.transaction
fun Table.initTable(database: Database) {
transaction(database) { SchemaUtils.createMissingTablesAndColumns(this@initTable) }
}

View File

@ -0,0 +1,34 @@
package com.insanusmokrassar.postssystem.utils.repos.exposed.onetomany
import com.insanusmokrassar.budgetmanager.core.utils.repo.onetomany.AbstractOneToManyExposedReadKeyValueRepo
import com.insanusmokrassar.budgetmanager.core.utils.repo.onetomany.ColumnAllocator
import com.insanusmokrassar.postssystem.utils.repos.OneToManyKeyValueRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractOneToManyExposedKeyValueRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
database: Database
) : OneToManyKeyValueRepo<Key, Value>, AbstractOneToManyExposedReadKeyValueRepo<Key, Value>(
keyColumnAllocator,
valueColumnAllocator,
database
) {
override suspend fun add(k: Key, v: Value) {
transaction(database) {
insert {
it[keyColumn] = k
it[valueColumn] = v
}
}
}
override suspend fun remove(k: Key, v: Value) {
transaction(database) { deleteWhere { keyColumn.eq(k).and(valueColumn.eq(v)) } }
}
override suspend fun clear(k: Key) {
transaction(database) { deleteWhere { keyColumn.eq(k) } }
}
}

View File

@ -0,0 +1,48 @@
package com.insanusmokrassar.budgetmanager.core.utils.repo.onetomany
import com.insanusmokrassar.postssystem.exposed.commons.paginate
import com.insanusmokrassar.postssystem.utils.repos.OneToManyReadKeyValueRepo
import com.insanusmokrassar.postssystem.utils.repos.pagination.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
typealias ColumnAllocator<T> = Table.() -> Column<T>
abstract class AbstractOneToManyExposedReadKeyValueRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
protected val database: Database
) : OneToManyReadKeyValueRepo<Key, Value>, Table() {
protected val keyColumn: Column<Key> = keyColumnAllocator()
protected val valueColumn: Column<Value> = valueColumnAllocator()
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 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()
}
}