diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fb0f8dae47..1192cbb3728 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ * `Versions`: * `Coroutines`: `1.4.2` -> `1.4.3` +* `Repos`: + * `Common` + * `Android`: + * New `blockingReadableTransaction`/`blockingWritableTransaction` + * Android databases realizations now use blocking transactions where it is possible ## 0.4.28 diff --git a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/DatabaseTransactions.kt b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/DatabaseTransactions.kt index 6d7821d20ca..b5ab920e1c1 100644 --- a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/DatabaseTransactions.kt +++ b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/DatabaseTransactions.kt @@ -32,7 +32,9 @@ private object ContextsPool { suspend fun use(block: suspend (CoroutineContext) -> T): T = acquireContext().let { try { - block(it) + safely { + block(it) + } } finally { freeContext(it) } @@ -48,11 +50,12 @@ class TransactionContext( } suspend fun SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T { - return coroutineContext[TransactionContext] ?.let { - withContext(it.databaseContext) { + coroutineContext[TransactionContext] ?.let { + return withContext(it.databaseContext) { block() } - } ?: ContextsPool.use { context -> + } + return ContextsPool.use { context -> withContext(TransactionContext(context) + context) { beginTransaction() safely( @@ -70,18 +73,18 @@ suspend fun SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T } } -inline fun SQLiteDatabase.inlineTransaction(block: SQLiteDatabase.() -> T): T { +inline fun SQLiteDatabase.inlineTransaction(crossinline block: SQLiteDatabase.() -> T): T { return when { inTransaction() -> block() else -> { beginTransaction() try { - block().also { - setTransactionSuccessful() - } + block().also { setTransactionSuccessful() } } finally { endTransaction() } } } } + +fun SQLiteDatabase.blockingTransaction(block: SQLiteDatabase.() -> T): T = inlineTransaction(block) diff --git a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/StandardSQLHelper.kt b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/StandardSQLHelper.kt index 591778d2a87..8979680cefe 100644 --- a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/StandardSQLHelper.kt +++ b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/StandardSQLHelper.kt @@ -31,6 +31,20 @@ class StandardSQLHelper( suspend fun readableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.readableTransaction(block) } +fun SQLiteOpenHelper.blockingWritableTransaction(block: SQLiteDatabase.() -> T): T { + return writableDatabase.blockingTransaction(block) +} +fun SQLiteOpenHelper.blockingReadableTransaction(block: SQLiteDatabase.() -> T): T { + return readableDatabase.blockingTransaction(block) +} + +fun StandardSQLHelper.blockingWritableTransaction(block: SQLiteDatabase.() -> T): T { + return sqlOpenHelper.blockingWritableTransaction(block) +} +fun StandardSQLHelper.blockingReadableTransaction(block: SQLiteDatabase.() -> T): T { + return sqlOpenHelper.blockingReadableTransaction(block) +} + suspend fun SQLiteOpenHelper.writableTransaction(block: suspend SQLiteDatabase.() -> T): T { return writableDatabase.transaction(block) } diff --git a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/crud/AbstractAndroidCRUDRepo.kt b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/crud/AbstractAndroidCRUDRepo.kt index bd1109b71ea..64440100acf 100644 --- a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/crud/AbstractAndroidCRUDRepo.kt +++ b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/crud/AbstractAndroidCRUDRepo.kt @@ -20,7 +20,7 @@ abstract class AbstractAndroidCRUDRepo( it.count }.toLong() - override suspend fun contains(id: IdType): Boolean = helper.readableTransaction { + override suspend fun contains(id: IdType): Boolean = helper.blockingReadableTransaction { select( tableName, null, diff --git a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/crud/AbstractMutableAndroidCRUDRepo.kt b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/crud/AbstractMutableAndroidCRUDRepo.kt index 3da89678146..b433ecf9da5 100644 --- a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/crud/AbstractMutableAndroidCRUDRepo.kt +++ b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/crud/AbstractMutableAndroidCRUDRepo.kt @@ -19,9 +19,10 @@ abstract class AbstractMutableAndroidCRUDRepo): List { - val indexes = helper.writableTransaction { - values.map { - insert(tableName, null, it.asContentValues()) + val valuesContentValues = values.map { it.asContentValues() } + val indexes = helper.blockingWritableTransaction { + valuesContentValues.map { + insert(tableName, null, it) } } return helper.readableTransaction { @@ -47,7 +48,7 @@ abstract class AbstractMutableAndroidCRUDRepo) { val deleted = mutableListOf() - helper.writableTransaction { + helper.blockingWritableTransaction { ids.forEach { id -> delete(tableName, "$idColumnName=?", arrayOf(id.asId)).also { if (it > 0) { @@ -64,7 +65,7 @@ abstract class AbstractMutableAndroidCRUDRepo>): List { + val contentValues = values.map { (id, value) -> id to value.asContentValues(id) } helper.writableTransaction { - values.forEach { (id, value) -> + contentValues.forEach { (id, contentValues) -> update( tableName, - value.asContentValues(id), + contentValues, "$idColumnName=?", arrayOf(id.asId) ) @@ -98,5 +100,5 @@ abstract class AbstractMutableAndroidCRUDRepo( private fun String.asKey(): Key = internalSerialFormat.decodeFromString(keySerializer, this) init { - runBlocking(DatabaseCoroutineContext) { - helper.writableTransaction { - createTable( - tableName, - internalId to internalIdType, - idColumnName to ColumnType.Text.NOT_NULLABLE, - valueColumnName to ColumnType.Text.NULLABLE - ) - } + helper.blockingWritableTransaction { + createTable( + tableName, + internalId to internalIdType, + idColumnName to ColumnType.Text.NOT_NULLABLE, + valueColumnName to ColumnType.Text.NULLABLE + ) } } override suspend fun add(toAdd: Map>) { val added = mutableListOf>() - helper.writableTransaction { + helper.blockingWritableTransaction { toAdd.forEach { (k, values) -> values.forEach { v -> insert( @@ -78,7 +76,7 @@ class OneToManyAndroidRepo( } override suspend fun clear(k: Key) { - helper.writableTransaction { + helper.blockingWritableTransaction { delete(tableName, "$idColumnName=?", arrayOf(k.asId())) }.also { if (it > 0) { @@ -88,7 +86,7 @@ class OneToManyAndroidRepo( } override suspend fun set(toSet: Map>) { - val (clearedKeys, inserted) = helper.writableTransaction { + val (clearedKeys, inserted) = helper.blockingWritableTransaction { toSet.mapNotNull { (k, _) -> if (delete(tableName, "$idColumnName=?", arrayOf(k.asId())) > 0) { k @@ -110,13 +108,13 @@ class OneToManyAndroidRepo( inserted.forEach { newPair -> _onNewValue.emit(newPair) } } - override suspend fun contains(k: Key): Boolean = helper.readableTransaction { + override suspend fun contains(k: Key): Boolean = helper.blockingReadableTransaction { select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use { it.count > 0 } } - override suspend fun contains(k: Key, v: Value): Boolean = helper.readableTransaction { + override suspend fun contains(k: Key, v: Value): Boolean = helper.blockingReadableTransaction { select( tableName, selection = "$idColumnName=? AND $valueColumnName=?", @@ -127,7 +125,7 @@ class OneToManyAndroidRepo( } } - override suspend fun count(): Long =helper.readableTransaction { + override suspend fun count(): Long =helper.blockingReadableTransaction { select( tableName ).use { @@ -135,7 +133,7 @@ class OneToManyAndroidRepo( } }.toLong() - override suspend fun count(k: Key): Long = helper.readableTransaction { + override suspend fun count(k: Key): Long = helper.blockingReadableTransaction { select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use { it.count } @@ -147,7 +145,7 @@ class OneToManyAndroidRepo( reversed: Boolean ): PaginationResult = count(k).let { count -> val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } - helper.readableTransaction { + helper.blockingReadableTransaction { select( tableName, selection = "$idColumnName=?", @@ -173,7 +171,7 @@ class OneToManyAndroidRepo( reversed: Boolean ): PaginationResult = count().let { count -> val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } - helper.readableTransaction { + helper.blockingReadableTransaction { select( tableName, limit = resultPagination.limitClause() @@ -198,7 +196,7 @@ class OneToManyAndroidRepo( reversed: Boolean ): PaginationResult = count().let { count -> val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } - helper.readableTransaction { + helper.blockingReadableTransaction { select( tableName, selection = "$valueColumnName=?", @@ -220,7 +218,7 @@ class OneToManyAndroidRepo( } override suspend fun remove(toRemove: Map>) { - helper.writableTransaction { + helper.blockingWritableTransaction { toRemove.flatMap { (k, vs) -> vs.mapNotNullA { v -> if (delete(tableName, "$idColumnName=? AND $valueColumnName=?", arrayOf(k.asId(), v.asValue())) > 0) {