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...
}
Related
I have a data class contains id and code.
And I have a List contains codes only.
how to insert the codes into table without making up ids?
Actually I do not need the id column at all, but it seems Room requires a primary key, and codes cannot be primary key.
Room:
#Entity(tableName = "raw_table")
data class Raw(
#PrimaryKey(autoGenerate = true)
var id: Long = 0L,
#ColumnInfo(name = "code")
var code: String = "",
...
List and loop:
val codeList : List<String> = ...
for (code in codeList){
// wrong here, I need the id, but I do not have ids.
RawDao.insert(code)
}
Create a Dao like (or amend an existing Dao to include the #Insert as below)
:-
#Dao
interface RawDao {
#Insert
fun insertManyRaws(raws: List<Raw>): List<Long>
}
Create the #Database as normal including abstract function to get the RawDao. e.g.
:-
#Database(entities = [Raw::class],version = 1)
abstract class RawDatabase: RoomDatabase() {
abstract fun getRawDao(): RawDao
}
You can then use something like :-
class MainActivity : AppCompatActivity() {
lateinit var db: RawDatabase
lateinit var dao: RawDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rawList: List<Raw> = arrayListOf(Raw(0L,"Test1"),Raw(0L,"Test2"),Raw(0L,"etc...."))
db = Room.databaseBuilder(this,RawDatabase::class.java,"raw.db")
.allowMainThreadQueries()
.build()
dao = db.getRawDao();
dao.insertManyRaws(rawList) //<<<<<<<<<< ADD All Raws at once
}
}
Running the above results in :-
i.e. The 3 Raw's have been added to the database, as seen by using AS's Database Inspector
Note the dao.insertManyRaws call returns, as a List, the inserted id's (if any are -1 then that Raw was not inserted)
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.
What is the best way to use coroutines with LiveData for selecting some data from database using Room.
This is My Dao class with suspended selection
#Dao
interface UserDao {
#Query("SELECT * from user_table WHERE id =:id")
suspend fun getUser(id: Long): User
}
Inside of View Model class I load user with viewModelScope.
Does it correct way to obtain user entity ?
fun load(userId: Long, block: (User?) -> Unit) = viewModelScope.launch {
block(dao.getUser(userId))
}
According developer android mentioned
val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend function.
emit(data)
}
This chunk of code does not work
Your Room must return LiveData.
Use instead:
#Dao
interface UserDao {
#Query("SELECT * from user_table WHERE id =:id")
fun getUser(id: Long): LiveData<User>
}
I have a ViewModel and I am using LiveData, so I have a DAO that return LiveData> and I can get this to work, but really what I would want it to first show data from Room database if there are some, and then when the webservice has returned new data (if there are any) then write that to database and then update the ViewModel with the latest data from the database. I got as far as returning the data firstly from the database and also writing the new data to the database in the background, but then how do I get the ViewModel to read/update with the new data from the database again?
Thank you
Søren
You can make your DAO return LiveData<Any>. It means that you can get notified about every change on that entity.
Assume that you have a User entity:
#Entity
data class User(
#PrimaryKey(autoGenerate = true) var uid: Int = 0,
#ColumnInfo(name = "name")
val name: String
)
And its related DAO looks like:
#Dao
interface UserDao {
#Query("SELECT * FROM user")
fun all(): LiveData<List<User>>
#Insert
suspend fun insert(vararg users: User)
}
So, you just need to expose the result of all to your view layer:
class UserViewModel : ViewModel() {
val users: LiveData<List<User>> = userDao.all()
}
class UserActivity : AppCompatActivity() {
private val viewModel by viewModels<UserViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.users.observe(this, Observer { users ->
// show data
})
}
}
Now if you insert a new User, Your observer will be called.
This is the basic idea of how to get updated data from your DAO. But in your case what you actually must do is to create a Repository and inside that do your business logic, provide offline-first data and then try to get data from network and update database.
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