Add: Simple DAOs (4/5 in replacing SQLite with Room)

This commit is contained in:
machiav3lli 2021-10-15 00:47:46 +02:00
parent d07a04d2db
commit 2d51310c08
5 changed files with 152 additions and 2 deletions

View File

@ -0,0 +1,89 @@
package com.looker.droidify.database
import android.database.SQLException
import androidx.room.*
@Dao
interface RepositoryDao {
@Insert
@Throws(SQLException::class)
fun insert(vararg repository: Repository)
@Update(onConflict = OnConflictStrategy.REPLACE)
fun update(vararg repository: Repository?)
fun put(repository: Repository) {
if (repository.id >= 0L) update(repository) else insert(repository)
}
@Query("SELECT * FROM repository WHERE _id = :id and deleted == 0")
fun get(id: Long): Repository?
@get:Query("SELECT * FROM repository WHERE deleted == 0 ORDER BY _id ASC")
val all: List<Repository>
@get:Query("SELECT _id, deleted FROM repository WHERE deleted != 0 and enabled == 0 ORDER BY _id ASC")
val allDisabledDeleted: List<Repository.IdAndDeleted>
@Delete
fun delete(repository: Repository)
@Query("DELETE FROM repository WHERE _id = :id")
fun deleteById(vararg id: Long): Int
@Update(onConflict = OnConflictStrategy.REPLACE)
fun markAsDeleted(id: Long) {
update(get(id).apply { this?.deleted = 1 })
}
}
@Dao
interface ProductDao {
@Query("SELECT COUNT(*) FROM product WHERE repository_id = :id")
fun countForRepository(id: Long): Long
@Query("SELECT * FROM product WHERE package_name = :packageName")
fun get(packageName: String): Product?
@Query("DELETE FROM product WHERE repository_id = :id")
fun deleteById(vararg id: Long): Int
}
@Dao
interface CategoryDao {
@Query(
"""SELECT DISTINCT category.name
FROM category AS category
JOIN repository AS repository
ON category.repository_id = repository._id
WHERE repository.enabled != 0 AND
repository.deleted == 0"""
)
fun getAll(): List<String>
@Query("DELETE FROM category WHERE repository_id = :id")
fun deleteById(vararg id: Long): Int
}
@Dao
interface InstalledDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
@Throws(SQLException::class)
fun insert(vararg installed: Installed)
@Query("SELECT * FROM installed WHERE package_name = :packageName")
fun get(packageName: String): Installed?
@Query("DELETE FROM installed WHERE package_name = :packageName")
fun delete(packageName: String)
}
@Dao
interface LockDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
@Throws(SQLException::class)
fun insert(vararg lock: Lock)
@Query("DELETE FROM lock WHERE package_name = :packageName")
fun delete(packageName: String)
}

View File

@ -351,6 +351,7 @@ object Database {
}
object RepositoryAdapter {
// Done in put
internal fun putWithoutNotification(repository: Repository, shouldReplace: Boolean): Long {
return db.insertOrReplace(shouldReplace, Schema.Repository.name, ContentValues().apply {
if (shouldReplace) {
@ -362,6 +363,7 @@ object Database {
})
}
// Done
fun put(repository: Repository): Repository {
val shouldReplace = repository.id >= 0L
val newId = putWithoutNotification(repository, shouldReplace)
@ -370,6 +372,7 @@ object Database {
return if (newId != repository.id) repository.copy(id = newId) else repository
}
// Done
fun get(id: Long): Repository? {
return db.query(
Schema.Repository.name,
@ -381,6 +384,8 @@ object Database {
.use { it.firstOrNull()?.let(::transform) }
}
// Done
// MAYBE signal has to be considered
fun getAll(signal: CancellationSignal?): List<Repository> {
return db.query(
Schema.Repository.name,
@ -389,6 +394,8 @@ object Database {
).use { it.asSequence().map(::transform).toList() }
}
// Done Pair<Long,Int> instead
// MAYBE signal has to be considered
fun getAllDisabledDeleted(signal: CancellationSignal?): Set<Pair<Long, Boolean>> {
return db.query(
Schema.Repository.name,
@ -408,6 +415,7 @@ object Database {
}
}
// Done
fun markAsDeleted(id: Long) {
db.update(Schema.Repository.name, ContentValues().apply {
put(Schema.Repository.ROW_DELETED, 1)
@ -415,6 +423,7 @@ object Database {
notifyChanged(Subject.Repositories, Subject.Repository(id), Subject.Products)
}
// Done
fun cleanup(pairs: Set<Pair<Long, Boolean>>) {
val result = pairs.windowed(10, 10, true).map {
val idsString = it.joinToString(separator = ", ") { it.first.toString() }
@ -442,6 +451,7 @@ object Database {
}
}
// get the cursor in the specific table. Unnecessary with Room
fun query(signal: CancellationSignal?): Cursor {
return db.query(
Schema.Repository.name,
@ -450,6 +460,7 @@ object Database {
).observable(Subject.Repositories)
}
// Unnecessary with Room
fun transform(cursor: Cursor): Repository {
return cursor.getBlob(cursor.getColumnIndex(Schema.Repository.ROW_DATA))
.jsonParse {
@ -461,6 +472,7 @@ object Database {
}
object ProductAdapter {
// Done
fun get(packageName: String, signal: CancellationSignal?): List<Product> {
return db.query(
Schema.Product.name,
@ -474,6 +486,7 @@ object Database {
).use { it.asSequence().map(::transform).toList() }
}
// Done
fun getCount(repositoryId: Long): Int {
return db.query(
Schema.Product.name, columns = arrayOf("COUNT (*)"),
@ -485,6 +498,7 @@ object Database {
.use { it.firstOrNull()?.getInt(0) ?: 0 }
}
// Complex left to wiring phase
fun query(
installed: Boolean, updates: Boolean, searchQuery: String,
section: ProductItem.Section, order: ProductItem.Order, signal: CancellationSignal?
@ -517,16 +531,17 @@ object Database {
builder += """MAX((product.${Schema.Product.ROW_COMPATIBLE} AND
(installed.${Schema.Installed.ROW_SIGNATURE} IS NULL OR $signatureMatches)) ||
PRINTF('%016X', product.${Schema.Product.ROW_VERSION_CODE})) FROM ${Schema.Product.name} AS product"""
builder += """JOIN ${Schema.Repository.name} AS repository
ON product.${Schema.Product.ROW_REPOSITORY_ID} = repository.${Schema.Repository.ROW_ID}"""
builder += """LEFT JOIN ${Schema.Lock.name} AS lock
ON product.${Schema.Product.ROW_PACKAGE_NAME} = lock.${Schema.Lock.ROW_PACKAGE_NAME}"""
if (!installed && !updates) {
builder += "LEFT"
}
builder += """JOIN ${Schema.Installed.name} AS installed
ON product.${Schema.Product.ROW_PACKAGE_NAME} = installed.${Schema.Installed.ROW_PACKAGE_NAME}"""
if (section is ProductItem.Section.Category) {
builder += """JOIN ${Schema.Category.name} AS category
ON product.${Schema.Product.ROW_PACKAGE_NAME} = category.${Schema.Product.ROW_PACKAGE_NAME}"""
@ -534,6 +549,7 @@ object Database {
builder += """WHERE repository.${Schema.Repository.ROW_ENABLED} != 0 AND
repository.${Schema.Repository.ROW_DELETED} == 0"""
if (section is ProductItem.Section.Category) {
builder += "AND category.${Schema.Category.ROW_NAME} = ?"
builder %= section.name
@ -541,18 +557,22 @@ object Database {
builder += "AND product.${Schema.Product.ROW_REPOSITORY_ID} = ?"
builder %= section.id.toString()
}
if (searchQuery.isNotEmpty()) {
builder += """AND ${Schema.Synthetic.ROW_MATCH_RANK} > 0"""
}
builder += "GROUP BY product.${Schema.Product.ROW_PACKAGE_NAME} HAVING 1"
if (updates) {
builder += "AND ${Schema.Synthetic.ROW_CAN_UPDATE}"
}
builder += "ORDER BY"
if (searchQuery.isNotEmpty()) {
builder += """${Schema.Synthetic.ROW_MATCH_RANK} DESC,"""
}
when (order) {
ProductItem.Order.NAME -> Unit
ProductItem.Order.DATE_ADDED -> builder += "product.${Schema.Product.ROW_ADDED} DESC,"
@ -563,6 +583,7 @@ object Database {
return builder.query(db, signal).observable(Subject.Products)
}
// Unnecessary with Room
private fun transform(cursor: Cursor): Product {
return cursor.getBlob(cursor.getColumnIndex(Schema.Product.ROW_DATA))
.jsonParse {
@ -575,6 +596,7 @@ object Database {
}
}
// Unnecessary with Room
fun transformItem(cursor: Cursor): ProductItem {
return cursor.getBlob(cursor.getColumnIndex(Schema.Product.ROW_DATA_ITEM))
.jsonParse {
@ -602,6 +624,7 @@ object Database {
}
object CategoryAdapter {
// Done
fun getAll(signal: CancellationSignal?): Set<String> {
val builder = QueryBuilder()
@ -620,6 +643,7 @@ object Database {
}
object InstalledAdapter {
// Done
fun get(packageName: String, signal: CancellationSignal?): InstalledItem? {
return db.query(
Schema.Installed.name,
@ -632,6 +656,7 @@ object Database {
).use { it.firstOrNull()?.let(::transform) }
}
// Done in insert
private fun put(installedItem: InstalledItem, notify: Boolean) {
db.insertOrReplace(true, Schema.Installed.name, ContentValues().apply {
put(Schema.Installed.ROW_PACKAGE_NAME, installedItem.packageName)
@ -644,8 +669,10 @@ object Database {
}
}
// Done in insert
fun put(installedItem: InstalledItem) = put(installedItem, true)
// Done in insert
fun putAll(installedItems: List<InstalledItem>) {
db.beginTransaction()
try {
@ -657,6 +684,7 @@ object Database {
}
}
// Done
fun delete(packageName: String) {
val count = db.delete(
Schema.Installed.name,
@ -668,6 +696,7 @@ object Database {
}
}
// Unnecessary with Room
private fun transform(cursor: Cursor): InstalledItem {
return InstalledItem(
cursor.getString(cursor.getColumnIndex(Schema.Installed.ROW_PACKAGE_NAME)),
@ -679,6 +708,7 @@ object Database {
}
object LockAdapter {
// Done in insert (Lock object instead of pair)
private fun put(lock: Pair<String, Long>, notify: Boolean) {
db.insertOrReplace(true, Schema.Lock.name, ContentValues().apply {
put(Schema.Lock.ROW_PACKAGE_NAME, lock.first)
@ -689,8 +719,10 @@ object Database {
}
}
// Done in insert (Lock object instead of pair)
fun put(lock: Pair<String, Long>) = put(lock, true)
// Done in insert (Lock object instead of pair)
fun putAll(locks: List<Pair<String, Long>>) {
db.beginTransaction()
try {
@ -702,12 +734,14 @@ object Database {
}
}
// Done
fun delete(packageName: String) {
db.delete(Schema.Lock.name, "${Schema.Lock.ROW_PACKAGE_NAME} = ?", arrayOf(packageName))
notifyChanged(Subject.Products)
}
}
// TODO add temporary tables
object UpdaterAdapter {
private val Table.temporaryName: String
get() = "${name}_temporary"

View File

@ -17,7 +17,11 @@ import androidx.room.TypeConverters
)
@TypeConverters(Converters::class)
abstract class DatabaseX : RoomDatabase() {
// TODO add the DAOs for the tables
abstract val repositoryDao: RepositoryDao
abstract val productDao: ProductDao
abstract val categoryDao: CategoryDao
abstract val installedDao: InstalledDao
abstract val lockDao: LockDao
companion object {
@Volatile
@ -40,4 +44,19 @@ abstract class DatabaseX : RoomDatabase() {
}
}
}
fun cleanUp(pairs: Set<Pair<Long, Boolean>>) {
val result = pairs.windowed(10, 10, true).map {
val ids = it.map { it.first }.toLongArray()
val productsCount = productDao.deleteById(*ids)
val categoriesCount = categoryDao.deleteById(*ids)
val deleteIds = it.filter { it.second }.map { it.first }.toLongArray()
repositoryDao.deleteById(*deleteIds)
productsCount != 0 || categoriesCount != 0
}
// Use live objects and observers instead
/*if (result.any { it }) {
com.looker.droidify.database.Database.notifyChanged(com.looker.droidify.database.Database.Subject.Products)
}*/
}
}

View File

@ -21,6 +21,13 @@ class Repository {
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
var data: Repository? = null
class IdAndDeleted {
@ColumnInfo(name = "_id")
var id = 0L
var deleted = 0
}
}
@Entity(primaryKeys = ["repository_id", "package_name"])

View File

@ -1,5 +1,6 @@
package com.looker.droidify.entity
// Redundant to Room's Installed
class InstalledItem(
val packageName: String,
val version: String,