mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-29 13:38:45 +00:00
android database improvements
This commit is contained in:
parent
5f38d9635d
commit
d1c6c7696a
@ -4,6 +4,11 @@
|
|||||||
|
|
||||||
* `Versions`:
|
* `Versions`:
|
||||||
* `Coroutines`: `1.4.2` -> `1.4.3`
|
* `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
|
## 0.4.28
|
||||||
|
|
||||||
|
@ -32,7 +32,9 @@ private object ContextsPool {
|
|||||||
|
|
||||||
suspend fun <T> use(block: suspend (CoroutineContext) -> T): T = acquireContext().let {
|
suspend fun <T> use(block: suspend (CoroutineContext) -> T): T = acquireContext().let {
|
||||||
try {
|
try {
|
||||||
block(it)
|
safely {
|
||||||
|
block(it)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
freeContext(it)
|
freeContext(it)
|
||||||
}
|
}
|
||||||
@ -48,11 +50,12 @@ class TransactionContext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T {
|
suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T {
|
||||||
return coroutineContext[TransactionContext] ?.let {
|
coroutineContext[TransactionContext] ?.let {
|
||||||
withContext(it.databaseContext) {
|
return withContext(it.databaseContext) {
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
} ?: ContextsPool.use { context ->
|
}
|
||||||
|
return ContextsPool.use { context ->
|
||||||
withContext(TransactionContext(context) + context) {
|
withContext(TransactionContext(context) + context) {
|
||||||
beginTransaction()
|
beginTransaction()
|
||||||
safely(
|
safely(
|
||||||
@ -70,18 +73,18 @@ suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> SQLiteDatabase.inlineTransaction(block: SQLiteDatabase.() -> T): T {
|
inline fun <T> SQLiteDatabase.inlineTransaction(crossinline block: SQLiteDatabase.() -> T): T {
|
||||||
return when {
|
return when {
|
||||||
inTransaction() -> block()
|
inTransaction() -> block()
|
||||||
else -> {
|
else -> {
|
||||||
beginTransaction()
|
beginTransaction()
|
||||||
try {
|
try {
|
||||||
block().also {
|
block().also { setTransactionSuccessful() }
|
||||||
setTransactionSuccessful()
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
endTransaction()
|
endTransaction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> SQLiteDatabase.blockingTransaction(block: SQLiteDatabase.() -> T): T = inlineTransaction(block)
|
||||||
|
@ -31,6 +31,20 @@ class StandardSQLHelper(
|
|||||||
suspend fun <T> readableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.readableTransaction(block)
|
suspend fun <T> readableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.readableTransaction(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> SQLiteOpenHelper.blockingWritableTransaction(block: SQLiteDatabase.() -> T): T {
|
||||||
|
return writableDatabase.blockingTransaction(block)
|
||||||
|
}
|
||||||
|
fun <T> SQLiteOpenHelper.blockingReadableTransaction(block: SQLiteDatabase.() -> T): T {
|
||||||
|
return readableDatabase.blockingTransaction(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> StandardSQLHelper.blockingWritableTransaction(block: SQLiteDatabase.() -> T): T {
|
||||||
|
return sqlOpenHelper.blockingWritableTransaction(block)
|
||||||
|
}
|
||||||
|
fun <T> StandardSQLHelper.blockingReadableTransaction(block: SQLiteDatabase.() -> T): T {
|
||||||
|
return sqlOpenHelper.blockingReadableTransaction(block)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun <T> SQLiteOpenHelper.writableTransaction(block: suspend SQLiteDatabase.() -> T): T {
|
suspend fun <T> SQLiteOpenHelper.writableTransaction(block: suspend SQLiteDatabase.() -> T): T {
|
||||||
return writableDatabase.transaction(block)
|
return writableDatabase.transaction(block)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ abstract class AbstractAndroidCRUDRepo<ObjectType, IdType>(
|
|||||||
it.count
|
it.count
|
||||||
}.toLong()
|
}.toLong()
|
||||||
|
|
||||||
override suspend fun contains(id: IdType): Boolean = helper.readableTransaction {
|
override suspend fun contains(id: IdType): Boolean = helper.blockingReadableTransaction {
|
||||||
select(
|
select(
|
||||||
tableName,
|
tableName,
|
||||||
null,
|
null,
|
||||||
|
@ -19,9 +19,10 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
|
|||||||
protected abstract suspend fun InputValueType.asContentValues(id: IdType? = null): ContentValues
|
protected abstract suspend fun InputValueType.asContentValues(id: IdType? = null): ContentValues
|
||||||
|
|
||||||
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
|
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
|
||||||
val indexes = helper.writableTransaction {
|
val valuesContentValues = values.map { it.asContentValues() }
|
||||||
values.map {
|
val indexes = helper.blockingWritableTransaction {
|
||||||
insert(tableName, null, it.asContentValues())
|
valuesContentValues.map {
|
||||||
|
insert(tableName, null, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return helper.readableTransaction {
|
return helper.readableTransaction {
|
||||||
@ -47,7 +48,7 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
|
|||||||
|
|
||||||
override suspend fun deleteById(ids: List<IdType>) {
|
override suspend fun deleteById(ids: List<IdType>) {
|
||||||
val deleted = mutableListOf<IdType>()
|
val deleted = mutableListOf<IdType>()
|
||||||
helper.writableTransaction {
|
helper.blockingWritableTransaction {
|
||||||
ids.forEach { id ->
|
ids.forEach { id ->
|
||||||
delete(tableName, "$idColumnName=?", arrayOf(id.asId)).also {
|
delete(tableName, "$idColumnName=?", arrayOf(id.asId)).also {
|
||||||
if (it > 0) {
|
if (it > 0) {
|
||||||
@ -64,7 +65,7 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
|
|||||||
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
|
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
|
||||||
val asContentValues = value.asContentValues(id)
|
val asContentValues = value.asContentValues(id)
|
||||||
if (asContentValues.keySet().isNotEmpty()) {
|
if (asContentValues.keySet().isNotEmpty()) {
|
||||||
helper.writableTransaction {
|
helper.blockingWritableTransaction {
|
||||||
update(
|
update(
|
||||||
tableName,
|
tableName,
|
||||||
asContentValues,
|
asContentValues,
|
||||||
@ -79,11 +80,12 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
|
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
|
||||||
|
val contentValues = values.map { (id, value) -> id to value.asContentValues(id) }
|
||||||
helper.writableTransaction {
|
helper.writableTransaction {
|
||||||
values.forEach { (id, value) ->
|
contentValues.forEach { (id, contentValues) ->
|
||||||
update(
|
update(
|
||||||
tableName,
|
tableName,
|
||||||
value.asContentValues(id),
|
contentValues,
|
||||||
"$idColumnName=?",
|
"$idColumnName=?",
|
||||||
arrayOf(id.asId)
|
arrayOf(id.asId)
|
||||||
)
|
)
|
||||||
@ -98,5 +100,5 @@ abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun count(): Long = helper.readableTransaction { select(tableName).use { it.count.toLong() } }
|
override suspend fun count(): Long = helper.blockingReadableTransaction { select(tableName).use { it.count.toLong() } }
|
||||||
}
|
}
|
@ -42,21 +42,19 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
private fun String.asKey(): Key = internalSerialFormat.decodeFromString(keySerializer, this)
|
private fun String.asKey(): Key = internalSerialFormat.decodeFromString(keySerializer, this)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
runBlocking(DatabaseCoroutineContext) {
|
helper.blockingWritableTransaction {
|
||||||
helper.writableTransaction {
|
createTable(
|
||||||
createTable(
|
tableName,
|
||||||
tableName,
|
internalId to internalIdType,
|
||||||
internalId to internalIdType,
|
idColumnName to ColumnType.Text.NOT_NULLABLE,
|
||||||
idColumnName to ColumnType.Text.NOT_NULLABLE,
|
valueColumnName to ColumnType.Text.NULLABLE
|
||||||
valueColumnName to ColumnType.Text.NULLABLE
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
||||||
val added = mutableListOf<Pair<Key, Value>>()
|
val added = mutableListOf<Pair<Key, Value>>()
|
||||||
helper.writableTransaction {
|
helper.blockingWritableTransaction {
|
||||||
toAdd.forEach { (k, values) ->
|
toAdd.forEach { (k, values) ->
|
||||||
values.forEach { v ->
|
values.forEach { v ->
|
||||||
insert(
|
insert(
|
||||||
@ -78,7 +76,7 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun clear(k: Key) {
|
override suspend fun clear(k: Key) {
|
||||||
helper.writableTransaction {
|
helper.blockingWritableTransaction {
|
||||||
delete(tableName, "$idColumnName=?", arrayOf(k.asId()))
|
delete(tableName, "$idColumnName=?", arrayOf(k.asId()))
|
||||||
}.also {
|
}.also {
|
||||||
if (it > 0) {
|
if (it > 0) {
|
||||||
@ -88,7 +86,7 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun set(toSet: Map<Key, List<Value>>) {
|
override suspend fun set(toSet: Map<Key, List<Value>>) {
|
||||||
val (clearedKeys, inserted) = helper.writableTransaction {
|
val (clearedKeys, inserted) = helper.blockingWritableTransaction {
|
||||||
toSet.mapNotNull { (k, _) ->
|
toSet.mapNotNull { (k, _) ->
|
||||||
if (delete(tableName, "$idColumnName=?", arrayOf(k.asId())) > 0) {
|
if (delete(tableName, "$idColumnName=?", arrayOf(k.asId())) > 0) {
|
||||||
k
|
k
|
||||||
@ -110,13 +108,13 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
inserted.forEach { newPair -> _onNewValue.emit(newPair) }
|
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 {
|
select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use {
|
||||||
it.count > 0
|
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(
|
select(
|
||||||
tableName,
|
tableName,
|
||||||
selection = "$idColumnName=? AND $valueColumnName=?",
|
selection = "$idColumnName=? AND $valueColumnName=?",
|
||||||
@ -127,7 +125,7 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun count(): Long =helper.readableTransaction {
|
override suspend fun count(): Long =helper.blockingReadableTransaction {
|
||||||
select(
|
select(
|
||||||
tableName
|
tableName
|
||||||
).use {
|
).use {
|
||||||
@ -135,7 +133,7 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
}.toLong()
|
}.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 {
|
select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use {
|
||||||
it.count
|
it.count
|
||||||
}
|
}
|
||||||
@ -147,7 +145,7 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
reversed: Boolean
|
reversed: Boolean
|
||||||
): PaginationResult<Value> = count(k).let { count ->
|
): PaginationResult<Value> = count(k).let { count ->
|
||||||
val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination }
|
val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination }
|
||||||
helper.readableTransaction {
|
helper.blockingReadableTransaction {
|
||||||
select(
|
select(
|
||||||
tableName,
|
tableName,
|
||||||
selection = "$idColumnName=?",
|
selection = "$idColumnName=?",
|
||||||
@ -173,7 +171,7 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
reversed: Boolean
|
reversed: Boolean
|
||||||
): PaginationResult<Key> = count().let { count ->
|
): PaginationResult<Key> = count().let { count ->
|
||||||
val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination }
|
val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination }
|
||||||
helper.readableTransaction {
|
helper.blockingReadableTransaction {
|
||||||
select(
|
select(
|
||||||
tableName,
|
tableName,
|
||||||
limit = resultPagination.limitClause()
|
limit = resultPagination.limitClause()
|
||||||
@ -198,7 +196,7 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
reversed: Boolean
|
reversed: Boolean
|
||||||
): PaginationResult<Key> = count().let { count ->
|
): PaginationResult<Key> = count().let { count ->
|
||||||
val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination }
|
val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination }
|
||||||
helper.readableTransaction {
|
helper.blockingReadableTransaction {
|
||||||
select(
|
select(
|
||||||
tableName,
|
tableName,
|
||||||
selection = "$valueColumnName=?",
|
selection = "$valueColumnName=?",
|
||||||
@ -220,7 +218,7 @@ class OneToManyAndroidRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||||
helper.writableTransaction {
|
helper.blockingWritableTransaction {
|
||||||
toRemove.flatMap { (k, vs) ->
|
toRemove.flatMap { (k, vs) ->
|
||||||
vs.mapNotNullA { v ->
|
vs.mapNotNullA { v ->
|
||||||
if (delete(tableName, "$idColumnName=? AND $valueColumnName=?", arrayOf(k.asId(), v.asValue())) > 0) {
|
if (delete(tableName, "$idColumnName=? AND $valueColumnName=?", arrayOf(k.asId(), v.asValue())) > 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user