Inheriting from a room DAO interface - android

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>
}

Related

How to set getAll and nukeData as inheritance in Android Room?

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?

Room livedata : crash on launch

Recently I've decided to start using LiveData in my Room database by adding it to ado methods and then observing them in ViewModel, then even though I updated DB version after making all the changes and made a migration, it throws this error on emulator launch
2022-07-03 15:35:17.723 4866-4891/com.example... E/SQLiteLog: (1) statement aborts at 1: [ROLLBACK;] cannot rollback - no transaction is active
2022-07-03 15:35:17.724 4866-4891/com.example... E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
Process: com.example..., PID: 4866
java.lang.RuntimeException: Exception while computing database live data.
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
Caused by: android.database.sqlite.SQLiteException: cannot rollback - no transaction is active (code 1 SQLITE_ERROR)
at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:709)
at android.database.sqlite.SQLiteSession.endTransactionUnchecked(SQLiteSession.java:441)
at android.database.sqlite.SQLiteSession.endTransaction(SQLiteSession.java:403)
at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:589)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:422)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:151)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:112)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:706)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:483)
at androidx.room.RoomDatabase.query(RoomDatabase.java:526)
at androidx.room.util.DBUtil.query(DBUtil.java:86)
at com.example...data.database.NoteDao_Impl$16.call(NoteDao_Impl.java:723)
at com.example...data.database.NoteDao_Impl$16.call(NoteDao_Impl.java:720)
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
at java.lang.Thread.run(Thread.java:920) 
My dao code :
#Dao
interface NoteDao {
#Delete
fun deleteFolder(folder: Folder)
#Query("UPDATE Note SET isStarred = :state WHERE id = :id")
fun updateNoteChecked(id : Int, state : Boolean)
#Query("DELETE FROM Note WHERE id = :id")
fun deleteNoteById(id : Int)
#Query("SELECT * FROM Note WHERE id = :id")
fun getNoteById(id : Int) : Note
#Query("SELECT title FROM Folder WHERE id = :id")
fun getFolderTitleById(id : Int) : String
#Query("SELECT * FROM folder WHERE id = :id")
fun getFolderById(id : Int) : Folder
#Query("UPDATE Folder SET title = :title WHERE id = :id")
fun updateFolderTitle(id : Int, title : String)
#Query("SELECT * FROM Folder WHERE id = :id")
fun getFolderWithNotesByFolderId(id : Int) : FolderWithNotes
#Query("SELECT * FROM Note WHERE folderId = :id ORDER BY isStarred")
fun getNotesByFolderId(id : Int) : LiveData<List<Note>>
#Query("SELECT * FROM note WHERE content LIKE :query ORDER BY isStarred")
fun searchAllNotes(query : String?) : LiveData<List<Note>>
#Query("SELECT * FROM note WHERE folderId = :id AND content LIKE :query ORDER BY isStarred" )
fun searchNotesByFolderId(query : String?, id : Int) : LiveData<List<Note>>
#Query("SELECT * FROM Note ORDER BY isStarred")
fun getAllNotes() : LiveData<List<Note>>
#Query("SELECT * FROM Folder WHERE id = :id")
fun getFolderWithNotes(id : Int) : List<FolderWithNotes>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertFolder(folder : Folder)
#Query("SELECT * FROM Folder")
fun getAllFolders() : LiveData<List<Folder>>
#Query("UPDATE folder SET noteCount = noteCount + 1 WHERE id = :id")
fun updateOnInsertNote(id : Int)
#Query("UPDATE folder SET noteCount = noteCount - 1 WHERE id = :id")
fun updateOnDeleteNote(id : Int)
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertNote(note : Note)
#Delete
suspend fun deleteNote(note : Note)
}
Database class :
#Database(entities = [Note::class, Folder::class] , version = 11 )
abstract class NoteDatabase : RoomDatabase() {
abstract fun NoteDao() : NoteDao
}
Data base hilt provide method :
#Provides
#Singleton
fun provideNoteDatabase(app : Application) : NoteDatabase{
return Room.databaseBuilder(
app ,
NoteDatabase::class.java,
"notes",
).fallbackToDestructiveMigration()
.addCallback(DatabaseCallback())
.build()
}
As per my comment, I suspect that the issue is within the callback (DatabaseCallback) and probably in the overidden onOpen method.
The reasoning is that the trace shows:-
that you have issued a query as per at androidx.room.RoomDatabase.query(RoomDatabase.java:526)
that this successfully opens the database as per at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
-and then appears to end a transaction (without committing the transaction). As Room is proven, then the most likely issue is that an attempt is being made to end a transaction when no transaction has been started and thus, due to the lack of the commit, there is nowhere/nothing to rollback to.

Not sure how to handle insert method's return type. public abstract java.lang.Object insert(#org.jetbrains.annotations.NotNull()

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
}

How to create a generic Repository class? (Android)

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

Room DB with coroutines insert callback

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>
}

Categories

Resources