I have
#Dao
interface ContactDao {
#Query("SELECT * FROM ContactItem")
fun getAll(): Flow<List<ContactItem>>
#Insert
fun insertAll(vararg todos: ContactItem)
#Delete
fun delete(todo: ContactItem)
#Update
fun update(note: ContactItem)
#Query("DELETE FROM ContactItem")
fun nukeTable()
}
Dao
interface TodoDao {
#Query("SELECT * FROM TodoItem")
fun getAll(): Flow<List<TodoItem>>
#Insert
fun insertAll(vararg todos: TodoItem)
#Delete
fun delete(todo: TodoItem)
#Update
fun update(note: TodoItem)
#Query("DELETE FROM TodoItem")
fun nukeTable()
}
And I can make some of them in base interface to shrink both of them
interface BaseDao<T> {
#Insert
fun insertAll(vararg obj: T)
#Delete
fun delete(obj: T)
#Update
fun update(obj: T)
}
#Dao
interface ContactDao: BaseDao<ContactItem> {
#Query("SELECT * FROM ContactItem")
fun getAll(): Flow<List<ContactItem>>
#Query("DELETE FROM ContactItem")
fun nukeTable()
}
#Dao
interface TodoDao: BaseDao<TodoItem> {
#Query("SELECT * FROM TodoItem")
fun getAll(): Flow<List<TodoItem>>
#Query("DELETE FROM TodoItem")
fun nukeTable()
}
But you can see that both getAll and nukeTable is still there. I hope to also make it into the BaseDao. Is there any way to do that?
Related
I am complete new to kotlin, and trying to build a notes app using MVVM architecture. But somewhere I go wrong and end up with this error. My kotlin version is 1.5.31
NoteDao.java:11: error: Not sure how to handle insert method's return type.
public abstract java.lang.Object insert(#org.jetbrains.annotations.NotNull()
NoteDao.kt
#Dao
interface NoteDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(note:Note): Note
#Delete
suspend fun delete(note: Note): Note
#Query("SELECT * FROM notes_table ORDER BY primary_key ASC")
fun getAllNotes(): LiveData<List<Note>>
}
Note.kt
#Entity(tableName = "notes_table")
class Note(#ColumnInfo(name = "note_text", typeAffinity = TEXT) val text: String){
#PrimaryKey(autoGenerate = true) #ColumnInfo(name="primary_key", typeAffinity = INTEGER) var id = 0
}
I am not sure how to handle this.
Try doing this:
#Dao
abstract class BaseDao<T : BaseEntity>(private val tableName: String) {
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract suspend fun insert(entity: T): Long
#Update
abstract suspend fun update(entity: T)
#Delete
abstract suspend fun delete(entity: T)
}
abstract class BaseEntity {
abstract val id: Long
}
I have following interface where I have created the standard crud methods and annotated the methods with insert, update, and delete.
interface BaseDao<T> {
#Insert
fun insert(table: T): Single<Long>
#Insert
fun insert(vararg table: T): Single<List<Long>>
#Update
fun update(table: T): Single<Int>
#Delete
fun delete(table: T): Single<Int>
}
I then create a interface for the DAO
#Dao
interface WeatherDao : BaseDao<WeatherTable> {
override fun insert(table: WeatherTable): Single<Long>
override fun insert(vararg table: WeatherTable): Single<List<Long>>
override fun update(table: WeatherTable): Single<Int>
override fun delete(table: WeatherTable): Single<Int>
#Query("SELECT * FROM weatherTable")
fun getAllWeather(): Single<List<WeatherTable>>
#Query("SELECT * FROM weatherTable WHERE id = :id LIMIT 1")
fun getWeatherById(id: Long): Single<WeatherTable>
#Query("SELECT count(*) FROM weatherTable")
fun count(): Single<Int>
}
When I compile I get a lot of error like this following:
error: An abstract DAO method must be annotated with one and only one of the following annotations: Insert,Delete,Query,Update,RawQuery
public abstract io.reactivex.Single<java.lang.Long> delete(#org.jetbrains.annotations.NotNull()
Because when I inherited from the interface. I have to manually add the #Insert, #Update, and #Delete.
Just wondering why these annontations are added automatically in the my WeatherDao interface.
So I now have to manually add them like this:
#Dao
interface WeatherDao : BaseDao<WeatherTable> {
#Insert
override fun insert(table: WeatherTable): Single<Long>
#Insert
override fun insert(vararg table: WeatherTable): Single<List<Long>>
#Update
override fun update(table: WeatherTable): Single<Int>
#Delete
override fun delete(table: WeatherTable): Single<Int>
#Query("SELECT * FROM weatherTable")
fun getAllWeather(): Single<List<WeatherTable>>
#Query("SELECT * FROM weatherTable WHERE id = :id LIMIT 1")
fun getWeatherById(id: Long): Single<WeatherTable>
#Query("SELECT count(*) FROM weatherTable")
fun count(): Single<Int>
}
Just wondering if I am using this wrong:
Following this google repo, you are not doing the abstractation correctly. To sum up, you do not need to have the inserts/updates/deletes in your #Dao Interface and it should be abstract.
interface BaseDao<T> {
/**
* Insert an object in the database.
*
* #param obj the object to be inserted.
*/
#Insert
fun insert(obj: T)
/**
* Insert an array of objects in the database.
*
* #param obj the objects to be inserted.
*/
#Insert
fun insert(vararg obj: T)
/**
* Update an object from the database.
*
* #param obj the object to be updated
*/
#Update
fun update(obj: T)
/**
* Delete an object from the database
*
* #param obj the object to be deleted
*/
#Delete
fun delete(obj: T)
}
#Entity(tableName = "data")
data class Data(#PrimaryKey val id: String, val value: String)
#Dao
abstract class DataDao : BaseDao<Data>() {
/**
* Get all data from the Data table.
*/
#Query("SELECT * FROM Data")
abstract fun getData(): List<Data>
}
in my Android app I use MVVM model with repositories
most of my DAO and Repositories classes are almost identical, so I figured I could reuse some code by creating generic equivalents that are later extended
this worked fine for my DAO classes:
#Dao
interface BaseDao<T> {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(vararg entity: T)
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertList(entities: List<T>)
#Update
suspend fun update(entity: T)
#Delete
suspend fun delete(entity: T)
}
#Dao
interface SettingDao : BaseDao<Setting> {
#Query("SELECT value FROM settings WHERE id = :settingName")
fun get(settingName: String): LiveData<String>
#Query("SELECT * FROM settings ORDER BY id ASC")
fun getAll(): LiveData<List<Setting>>
#Query("DELETE FROM settings")
suspend fun deleteAll()
}
now I want to do something similar for repository, but I cannot figure out how
this is what I've tried (I get error java.lang.ClassCastException: java.lang.Object[] cannot be cast to: Setting[]) in BaseRepository$insert$2.invokeSuspend
abstract class BaseRepository<T>(private val baseDao: BaseDao<T>) {
suspend fun update(entity: T) {
withContext(Dispatchers.IO) { baseDao.update(entity) }
}
suspend fun insert(entity: T) {
withContext(Dispatchers.IO) { baseDao.insert(entity) }
}
suspend fun delete(entity: T) {
withContext(Dispatchers.IO) { baseDao.delete(entity) }
}
}
class SettingRepository(private val settingDao: SettingDao) : BaseRepository<Setting>(settingDao) {
val company = settingDao.get("company")
val ip = settingDao.get("ip")
suspend fun deleteAll() {
withContext(Dispatchers.IO) { settingDao.deleteAll() }
}
}
My intuition is that I have to adjust this bit private val baseDao: BaseDao<T> in BaseRepository, but I cannot figure out how
And 2nd question - is there a way to get a generic table name in DAO, so I could put
#Query("DELETE FROM settings")
suspend fun deleteAll()
in the generic interface as well (in this case replace settings with entity's tableName
learning Kotlin, proper app structure and stuff... Again... :)
So i have the following:
Dao
Repository
ViewModel
Dao Code
#Dao
interface ItemDao {
#Query("SELECT * from item_table ORDER BY Name ASC")
fun getSortedItems(): LiveData<List<Item>>
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(item: Item)
#Query("DELETE FROM item_table")
suspend fun deleteAll()
}
Repository
class ItemRepository (private val itemDao: ItemDao) {
val allItems: LiveData<List<Item>> = itemDao.getSortedItems()
suspend fun insert(item: Item) {
itemDao.insert(item)
}
}
ViewModel
class ItemViewModel(application: Application) : AndroidViewModel(application) {
private val repository: ItemRepository
val allItems: LiveData<List<Item>>
init {
val itemsDao = ItemDataBase.getDatabase(application, viewModelScope).itemDao()
repository = ItemRepository(itemsDao)
allItems = repository.allItems
}
fun insert(item: Item) = viewModelScope.launch {
repository.insert(item)
}
}
I need to get current amount of rows in a table and i was thinking of something like this:
add to Dao
#Query("SELECT COUNT(name) FROM item_table")
fun getAmount(): LiveData<Int>
and then in Repo
fun getAmount(): LiveData<Int>{
return itemDao.getAmount()
}
and the same in ViewModel
fun getAmount(): LiveData<Int> {
return repository.getAmount()
}
But again, should i also use an observer in Activity to get this value? Or, on the other hand, can i use just Int, while getting the table from DB as LiveData?
I am using RoomDB with Coroutines. My code looks like below -
#Dao
interface AccountDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAccountData(accountModel: AccountModel)
#Query("DELETE FROM accountTable")
suspend fun deleteAccountData()
#Query("SELECT * FROM accountTable")
suspend fun getAccountData(): Deferred<AccountModel>
}
//From my class
override suspend fun retrieveAccountData(): AccountModel {
return accountDao.getAccountData().await()
}
How do I return or what do I return for insert or delete from the DAO so that I know insert or delete was successful?
If the #Insert method receives only 1 parameter, it can return a Long, which is the new rowId for the inserted item. If the parameter is an array or a collection, it should return Long[] or List<Long> instead.
#Delete method returns an Int indicating the number of rows removed from the database.
So, your Dao should be like following:
#Dao
interface AccountDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAccountData(accountModel: AccountModel): Long
#Delete
suspend fun deleteAccountData(accountModel: AccountModel): Int
#Query("SELECT * FROM accountTable")
suspend fun getAccountData(): Deferred<AccountModel>
}