Room: Conflicting Declarations - android

I would like to add value, date and details to the current pb. I am receiving an error 'conflicting declaration' in the database for pbInfo. How should I fix this error?
#Entity(tableName = "pb_table")
data class Pb(#PrimaryKey
val pb: String)
#Entity
data class PbInfo(#PrimaryKey
var value: Double,
var date: Int,
var details: String)
#Dao
interface PbInfoDao {
#Insert
fun update(vararg pbInfo: PbInfo): LongArray
INSTANCE?.let { database ->
scope.launch {
populateDatabase(database.pbDao(), database.pbInfo())
}
}
}
suspend fun populateDatabase(pbDao: PbDao, pbInfoDao: PbInfoDao) {
pbDao.deleteAll()
var pb = Pb("Squat")
pbDao.insert(pb)
var pbInfo = PbInfo(122.5, 28, "I was feeling on top form today!")

First of all, you have two Entities in a single class (possibly the conflict)
So, add separate class for separate Entity.
Then, in your RoomDatabase abstract class, add two Entity Classes like this (and also create separate Dao interface classes):
#Database(entities = [(Pb::class), (Pbinfo::class)] ,version = 2)
abstract class YourRoomDatabaseClass: RoomDatabase(){
...
abstract fun pbDao() : PbDao
abstract fun pbinfoDao(): PbinfoDao
...
}
This should solve the conflicting of Entity classes. I have a single database with two Entities just like this and running without any problems. (Please mind me because I don't know Kotlin Syntax)

Use this
#Insert(onConflict = OnConflictStrategy.REPLACE)
instead of
#Insert

Related

Multiple Local Databases in Android Kotlin Using Room and ViewModel

How is possible to add multiple databases in room using ViewModel class? Do I need a different ViewModel class and a different #Entity and a different #Dao for each database, or can I use the same #Entity and #Dao of another database, sorry if the question is pretty obvious, thanks a lot in advance
I tried different Databases with the same #Entity, #Dao and ViewModel, Didn't work
I also tried Different everything, the database appeared but i couldn't query it, please help :(
Yes, you will need a separate Entity class, and Dao class for each database in Room. This is because each database will have its own set of data, and the Entity and Dao classes are used to define the schema and provide access to the data in the database.
// Define the Entity class for each database
#Entity(tableName = "data1")
data class Data1(
#PrimaryKey(autoGenerate = true) val id: Int,
val data: String
)
#Entity(tableName = "data2")
data class Data2(
#PrimaryKey(autoGenerate = true) val id: Int,
val data: String
)
// Define the Dao class for each database
#Dao
interface Data1Dao {
#Query("SELECT * FROM data1")
fun getAll(): List<Data1>
#Insert
fun insert(data: Data1)
// Other database operations...
}
#Dao
interface Data2Dao {
#Query("SELECT * FROM data2")
fun getAll(): List<Data2>
#Insert
fun insert(data: Data2)
// Other database operations...
}
// Define the ViewModel class for each database
class MyViewModel1: ViewModel() {
val db1: Database1
val db2: Database2
init {
db1 = Room.databaseBuilder(context, Database1::class.java,
"db1").build()
db2 = Room.databaseBuilder(context, Database2::class.java,
"db2").build()
}
fun getDataFromDb1(): List<Data1> {
return db1.data1Dao().getAll()
}
fun getDataFromDb2(): List<Data2> {
return db2.data2Dao().getAll()
}
// Other database operations...
}

Room partial update

I am trying to update partial fields in my Android Room database.
I am using #Update with a partial class:
#Update(entity = BehaviorDataSent::class)
fun update(obj: BehaviorDataSentUpdate)
#Entity(tableName = "BehaviorDataSent")
data class BehaviorDataSent(#PrimaryKey val actionTime: Long, val netName: String? = null, val savedTime: Long = 0, val sentTime: Long? = null)
data class BehaviorDataSentUpdate(val actionTime: Long, val sentTime: Long )
However, I get an error saying that the update method must have a body.
What have I missed?
It would appear that the #Update is in an abstract class rather than an interface. As such it needs to be an abstract function (or have a body).
so :-
#Update(entity = BehaviorDataSent::class)
abstract fun update(obj: BehaviorDataSentUpdate)
or change abstract class .... to interface
and/or you could use :-
#Query("UPDATE behaviordatasent SET sentTime=:sentTime WHERE actionTime=:actionTime")
abstract fun update(sentTime: Long, actionTime: Long)
in which case the BehaviourDataSentUpdate class is not required.
if in an interface then abstract should not be coded.
i.e. in an interface abstract is implied and thus does not need to be coded.However, in an abstract class functions without bodies need to be abstract and thus you can also have non-abstract functions that then need a body.

Room - queries in generic base class (especially flow queries)

In room, it seems to be impossible to use annotation based setups in a generic class with variable based data or with provided classes - the result is that there is no workaround to define queries with Flow inside an abstract generic base class.
Is that really try?
Examples 1 - CAN BE SOLVED
Define a query which contains a table name that is defined by a class variable
#Dao
abstract class BaseDao<Item : IItem, ItemWithRef> {
abstract val tableName: String
// DOES NOT WORK - because table name is not compile-time constant
#Transaction
#Query("select * from ${tableName}")
abstract suspend fun loadAll(): List<ItemWithRef>
// SOLUTION
private val rawQueryLoadAll
get() = "SELECT * FROM $tableName"
#Transaction
#RawQuery
protected abstract suspend fun loadAll(query: SimpleSQLiteQuery): List<ItemWithRef>
suspend fun loadAll(): List<ItemWithRef> = loadAll(queryLoadAll)
}
Examples 2 - CAN NOT BE SOLVED?
Define flow queries which contains a table name that is defined by a class variable
Here the problem is, that #RawQuery needs to know the queries classes - can this somehow be solved as well?
#Dao
abstract class BaseDao<Item : IItem, ItemWithRef> {
abstract val tableName: String
// all 3 possibilities DO NOT WORK
// - because `#RawQuery` needs to know that it handles `ItemWithRef::class`
// - because the table name is not constant
// DOES NOT WORK
#Transaction
#Query("select * from ${tableName}")
abstract suspend fun flowAll(): Flow<List<ItemWithRef>>
// DOES NOT WORK
#Transaction
#RawQuery
protected abstract fun flowAll(query: SimpleSQLiteQuery): Flow<List<ItemWithRef>>
fun flowAll(): Flow<List<ItemWithRef>> = flowAll(queryLoadAll)
// DOES NOT WORK
#Transaction
#RawQuery(observedEntities = arrayOf(ItemWithRef::class))
protected abstract fun flowAll(query: SimpleSQLiteQuery): Flow<List<ItemWithRef>>
fun flowAll(): Flow<List<ItemWithRef>> = flowAll(queryLoadAll)
}
Question
I'm fine with the workaround for example 1 but is there any workaround to also define a Flow raw query in a base class somehow?

ROOM Single Database Multiple Entities, Insert Query for different Tables

I have a small query, I have been confused on how to insert an object into a different entity.
I tried a work around doing:
#Query("INSERT INTO card_table VALUES(:id, :pid, :question, :answer, :type, :completed)")
suspend fun insertCard(id: UUID, pid: UUID, question: String, answer: String, type: CardType, completed: Boolean)
However, this has not worked.
My question is, how do I insert an object into different entity tables with a single database.
Note -> The insert() query works for inserting decks into the deck_table. The issue correlates to the card_table as I am unsure on how to reference it.
Thanks
database
#Insert(onConflict = OnConflictStrategy.ABORT)
suspend fun insert(deck: Deck)
#Insert
suspend fun insertCard(card: FlashCard)
#Database(entities = [Deck::class, FlashCard::class], version = 8, exportSchema = false)
#TypeConverters(DeckTypeConverters::class)
abstract class FlashCardDB : RoomDatabase() {
abstract fun DeckDAO(): DeckDAO
companion object {
private var INSTANCE: FlashCardDB? = null
fun getDatabase(context: Context, scope: CoroutineScope): FlashCardDB {
val tmpInstance = INSTANCE
if (tmpInstance != null) return tmpInstance
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
FlashCardDB::class.java,
"flash_cards_database"
).build() //.fallbackToDestructiveMigration() will causes users to loose data during migrations (use only for testing purposes)
INSTANCE = instance
return instance
}
tables
#Entity(tableName = "deck_table")
#Parcelize
data class Deck(
#PrimaryKey #ColumnInfo(name = "id") val id: UUID = UUID.randomUUID(),
#ColumnInfo(name = "title") var title: String,
#ColumnInfo(name = "date") var date: Calendar,
#ColumnInfo(name = "completed") var completed: Boolean
) : Parcelable {
#Entity(tableName = "card_table")
#Parcelize
data class FlashCard(
#PrimaryKey #ColumnInfo(name = "id") val id: UUID = UUID.randomUUID(),
#ColumnInfo(name = "parent_id") var pid: UUID,
#ColumnInfo(name = "question") var question: String,
#ColumnInfo(name = "answer") var answer: String,
#ColumnInfo(name = "type") var type: CardType,
#ColumnInfo(name = "completed") var completed: Boolean)
: Parcelable {
}
My question is, how do I insert an object into different entity tables with a single database
To insert object you need to put method with #Insert in interface or abstract class with #Dao
When you build your project - Room will generate implementation of your #Dao-interface. It would be plain class, but written in Java (for DeckDAO interface generated class would be DeckDAO_Impl.java). Also Room will implement #Insert-method (since you tell in this method what object you want to insert and Room knows its corresponding table's name). Let's say (to be simple) that implementation of this method would be executing query insert into _tableName_ values (...).
#Insert
suspend fun insert(deck: Deck) <-- Room will generate implementation "insert into deck_table values (..."
#Insert
suspend fun insert(card: FlashCard) <-- Room will generate implementation "insert into card_table values (..."
To get access to this implemented class' methods you should put your dao-interface in abstract class that extends Database. After build Room will make for you implementation of this abstract class and you can invoke method insert in your example with just:
FlashCardDB.getDatabase().DeckDAO().insert(deck)
or
FlashCardDB.getDatabase().DeckDAO().insert(card)
It's not necessary to create separate dao-interface for each entity. In theory you could use single dao-interface with all insert/delete/update/query-methods. Then all you need is to get access to all these methods through single FlashCardDB.getDatabase().DeckDAO() endpoint. You can put there 10 insert-methods with the same insert name but they should differ with parameter's type (it's just a method overloading) or you could use different method's names for clarity.
Though in practice (and in most tutorials) insert-delete-update-query methods for different entities for convenience are placed in different dao-interfaces. To achieve that you should put all these dao-interfaces to Database class. Let's say:
abstract class FlashCardDB : RoomDatabase() {
abstract fun deckDAO(): DeckDAO
abstract fun cardDAO(): CardDAO
So decision is up to you.
If i'm getting your question correctly you want to insert the card data into the database right? What you need to do is have a DAO for each database model. So your Card database model will have its own DAO:
#Dao
public abstract class CardDao {
#Insert
fun insert(card: Card)
}
#Database(entities = [Deck::class, FlashCard::class], version = 8, exportSchema = false)
#TypeConverters(DeckTypeConverters::class)
abstract class FlashCardDB : RoomDatabase() {
abstract fun deckDao(): DeckDao
abstract fun cardDao(): CardDao
...
Kindly let me know if this answers your question.

error: Type of the parameter must be a class annotated with #Entity or a collection/array of it

I have looked at other posts regarding the same, but i don't follow what mistake i must be doing.
I have the same name declared in Entity for the class as well as in Database declaration file too.
I'm also passing the same type of parameter as the entity class name, and still i'm getting this error thrown and it is not compiling.
Here's my code. TIA
#Entity(tableName = "current_task_table")
data class CurrentTask (
#PrimaryKey(autoGenerate = true) val uid: Int,
#ColumnInfo(name = "task_name") val taskName: String
)
#Dao
interface CurrentTaskDao {
#Query("SELECT * FROM current_task_table")
fun getAllCurrentTask(): LiveData<List<CurrentTask>>
#Insert
suspend fun insertCurrentTask(currentTask: CurrentTask)
#Query("DELETE FROM current_task_table")
fun deleteAllCurrentTasks()
}
#Database(entities = [CurrentTask::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract val currentTaskDao: CurrentTaskDao
companion object {
#Volatile
private var instance: AppDatabase? = null
fun getInstance(context: Context): AppDatabase? {
if (instance == null) {
synchronized(this) {
instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java, "app_database"
)
.fallbackToDestructiveMigration()
.build()
}
}
return instance
}
}
}
Fixed it by removing suspend from DAO
You cannot use suspend methods for DAO. Suspend function processed in compile time and compiler changes the signature of this function (different return type, an additional argument for state machine callback) to make it non-blocking.
Room waits for particular method signature to generate code. So, until Room doesn't support coroutines directly, you cannot use suspend function for DAO.
Found my answer here:
Error with Room dao class when using Kotlin coroutines
Removing the suspend is not necessary.
I solve this problem by this link.
Non-identification #Entity annotation
You need only have a small change in your build.gradle
please change this line
kapt "androidx.room:room-compiler:$room"
to this:
annotationProcessor "androidx.room:room-compiler:$room"
You can use suspend form in your DAO by this way

Categories

Resources