After inserting data into RoomDB when I fetch it using mindValleyDao.getCategories().value It returns null
DatabaseClass
#Database(entities = arrayOf(CategoryBO::class), version = 1, exportSchema = false)
abstract class MindValleyDatabase : RoomDatabase(){
abstract fun mindValleyDao(): MindValleyDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
#Volatile
private var INSTANCE: MindValleyDatabase? = null
fun getDatabase(context: Context): MindValleyDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
MindValleyDatabase::class.java,
"mindvalley_database"
).allowMainThreadQueries()
.fallbackToDestructiveMigration().build()
INSTANCE = instance
return instance
}
}
}
}
CategoryBO.kt
#Entity(tableName = "CategoryEntity")
data class CategoryBO( #PrimaryKey(autoGenerate = true) val id:Int, val name:String)
Doa
#Dao
interface MindValleyDao {
#Query("SELECT * from CategoryEntity ORDER BY id ASC")
fun getCategories(): LiveData<List<CategoryBO>>
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(categoryBO: CategoryBO)
//suspend fun insert(categoryBO: CategoryBO)
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(categoryBOList: List<CategoryBO>)
}
I am testing it by inserting Category and fetching list of categories like
class MindValleyViewModelNew #Inject constructor() : BaseViewModel() {
var categoryList: MutableLiveData<List<CategoryBO>> = MutableLiveData()
private lateinit var mindValleyDao:MindValleyDao
fun loadDatabase(mContext:Context){
mindValleyDao = MindValleyDatabase.getDatabase(mContext).mindValleyDao()
GlobalScope.launch(Dispatchers.IO) {
mindValleyDao.insert(CategoryBO(0,"first item"))
val cats = mindValleyDao.getCategories().value
categoryList.postValue(cats)
}
}
}
mindValleyDao.getCategories() has return type is LiveData, that's why it query value async, you shouldn't call .value
LiveData type in Room should only use for observe,
If you want to get value, modify your code to fun getCategories(): List<CategoryBO> instead
Related
I have a simple room database with LiveData, i want to get (cartPriceI: Double) values by id to use it within specific methods.
CartItemsDatabase.kt
#Parcelize
#Entity(tableName = "cart_table")
data class CartItemsDatabase(
#PrimaryKey(autoGenerate = true) var cid: Int,
#ColumnInfo(name = "titleD") var cartTitle: String?,
#ColumnInfo(name = "imageD") var cartImage: Bitmap?,
#ColumnInfo(name = "priceLD") var cartPriceL: Double?,
#ColumnInfo(name = "priceID") var cartPriceI: Double?,
#ColumnInfo(name = "itemNumD") var cartNum: Int?,
#ColumnInfo(name = "descriptionD") var cartDes: String?): Parcelable
CartItemsDAO.kt
#Dao
interface CartItemsDAO {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addItem(cartItemsDatabase: CartItemsDatabase)
#Update
suspend fun updateCart(cartItemsDatabase: CartItemsDatabase)
#Query("SELECT * FROM cart_table ORDER BY cid ASC")
fun readAllData(): LiveData<List<CartItemsDatabase>>
#Delete
fun delete(cartItems: CartItemsDatabase)
}
CartRepository.kt
class CartRepository(private val cartItemsDAO: CartItemsDAO) {
val readAllData: LiveData<List<CartItemsDatabase>> = cartItemsDAO.readAllData()
suspend fun addItem(cartItemsDatabase: CartItemsDatabase){
cartItemsDAO.addItem(cartItemsDatabase)
}
suspend fun updateCart(cartItemsDatabase: CartItemsDatabase){
cartItemsDAO.updateCart(cartItemsDatabase)
}
}
CartViewModel.kt
class CartViewModel(application: Application): AndroidViewModel(application) {
val readAllData: LiveData<List<CartItemsDatabase>>
private val repository: CartRepository
init {
val cartDao = AppDatabase.getDatabase(application).cartDao()
repository = CartRepository(cartDao)
readAllData = repository.readAllData
}
fun addItem(cartItemsDatabase: CartItemsDatabase){
viewModelScope.launch(Dispatchers.IO){
repository.addItem(cartItemsDatabase)
}
}
fun updateCart(cartItemsDatabase: CartItemsDatabase){
viewModelScope.launch(Dispatchers.IO) {
repository.updateCart(cartItemsDatabase)
}
}
}
AppDatabase.kt
#Database(entities = [CartItemsDatabase::class], version = 1, exportSchema = false)
#TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun cartDao(): CartItemsDAO
companion object {
#Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
val tempInstance = INSTANCE
if (tempInstance != null){
return tempInstance
}
synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"cart_database"
).build()
INSTANCE = instance
return instance
}
}
private fun buildDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"cartItems_database"
)
.build()
}
}
}
I'm a beginner, i want to know if there is possible way to do that and retrieve specific values from database (ex. cartPriceI in Double) by id!
You can query the database by the given id and return it asynchronously either with a suspend fun or LiveData/Flow
In the CartItemsDAO:
#Query("SELECT cartPriceI FROM cart_table WHERE cid = :id")
suspend fun getPrice(id: Int): Double
#Query("SELECT cartPriceI FROM cart_table WHERE cid = :id")
fun getPrice(id: Int): LiveData<Double>
You can get a specific model by SELECT with WHERE statement
#Query("SELECT * FROM cart_table WHERE cid == : cid")
fun getCartItemsDatabase(cid: String): LiveData<CartItemsDatabase?>
Or you can Return a subset of a table's columns
#Query("SELECT priceID FROM cart_table WHERE cid == : cid")
fun getCartItemPrice(cid: String): LiveData<Double>
ok so im quite new to using room, ive looked in a lot of places but find it hard to find my answer, alot of places ive looked around seem to have outdated information.
I have setup a room database, i have all the information for it set up as well as a viewmodel to send the infomration from. However i can not for the life of me figure out how to process the information in to android compose.
here is what i have done,any feedback would be good.
#Entity
data class Notes(
#PrimaryKey val uid: Int?,
#ColumnInfo(name = "header") val header: String?,
#ColumnInfo(name = "note") val note: String?
)
#Dao
interface NotesDao {
#Query("SELECT * FROM notes")
fun getAll(): LiveData<List<Notes>>
#Insert (onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(notes: Notes)
#Delete
suspend fun delete(notes: Notes)
}
#Database(entities = [Notes::class], version = 1)
abstract class NotesAppDatabase : RoomDatabase() {
abstract fun notesDao(): NotesDao
companion object {
#Volatile
private var INSTANCE: NotesAppDatabase? = null
fun getDatabase(context: Context): NotesAppDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
NotesAppDatabase::class.java,
"items_database"
)
// Wipes and rebuilds instead of migrating if no Migration object.
// Migration is not part of this codelab.
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
// return instance
instance
}
}
}
}
class NotesRepository (private val notesDao: NotesDao) {
val readAllData: LiveData<List<Notes>> = notesDao.getAll()
suspend fun addNote(note: Notes){
notesDao.insert(note)
}
}
class NotesRepoViewModel (application: Application): AndroidViewModel(application) {
private val repo: NotesRepository
private val allNotes: LiveData<List<Notes>>
init {
val dao = NotesAppDatabase.getDatabase(application).notesDao()
repo = NotesRepository(dao)
allNotes = repo.readAllData
}
fun notes(notes: Notes){
viewModelScope.launch {
repo.addNote(notes)
}
}
}
There is an event in my project and there are timelines in this event. Each event has its own timelines. I want to list the timelines of each event with the codes I have written, but I cannot access it via the adapter.
My entities;
Event:
#Parcelize
#Entity(tableName = "event_table")
data class Event(
#PrimaryKey(autoGenerate = true)
val eventId: Int,
val eventName: String,
val eventCategory: String
): Parcelable
Timeline:
#Parcelize
#Entity(tableName = "timeline_table")
data class Timeline(
#PrimaryKey(autoGenerate = true)
val TimeLineId: Int,
val TimeLineAccomplish: String,
val TimeLineDetails: String,
val TimeLineDate: String,
val eventCreatorId: Int
): Parcelable
EventAndTimeline:
data class EventAndTimeline(
#Embedded val event: Event,
#Relation(
parentColumn = "eventId",
entityColumn = "eventCreatorId"
)
val timeline: List<Timeline>
)
Database:
#Database(entities = [Timeline::class, Event::class], version = 1, exportSchema = false)
abstract class TimeLineDatabase: RoomDatabase() {
abstract fun timelineDao(): TimelineDao
abstract fun eventDao(): EventDao
companion object{
#Volatile
private var INSTANCE: TimeLineDatabase? = null
fun getDatabase(context: Context): TimeLineDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
TimeLineDatabase::class.java,
"timeline_database"
)
.build()
INSTANCE = instance
return instance
}
}
}
}
Dao:
#Dao
interface TimelineDao{
#Transaction
#Query("SELECT * FROM event_table")
fun getEventsWithTimelines(): LiveData<List<EventAndTimeline>>
}
Repository:
class TimelineRepository(private val timelineDao: TimelineDao) {
val readAllData: LiveData<List<EventAndTimeline>> = timelineDao.getEventsWithTimelines()
}
ViewModel:
class TimeLineViewModel(application: Application): AndroidViewModel(application) {
val readAllData: LiveData<List<EventAndTimeline>>
private val timelineRepository: TimelineRepository
init {
val timelineDao = TimeLineDatabase.getDatabase(application).timelineDao()
timelineRepository = TimelineRepository(timelineDao)
readAllData = timelineRepository.readAllData
}
Finally: In my List adapter i want to list timelines of event. When i didnt have a relation i reach all timelines with:
holder.itemView.findViewById<TextView>(R.id.timelineDetails_txt).text
currentItem.TimeLineDetails
but now I can't see timelines variables(Like TimeLineDetails, TimeLineAccomplish etc.).
You need to write a query like the below example.
interface UserBookDao {
#Query(
"SELECT user.name AS userName, book.name AS bookName " +
"FROM user, book " +
"WHERE user.id = book.user_id"
)
fun loadUserAndBookNames(): LiveData<List<UserBook>>
// You can also define this class in a separate file.
data class UserBook(val userName: String?, val bookName: String?)
}
you can check more examples in below link.
https://developer.android.com/training/data-storage/room/relationships
I'm new with Room Database and I would like to extract from one database an array of int.
I think that the problem is in the code of the fragment, when I would like to use the function "stationlist" to obtain an array of the codes of stations present in database. The output "StationList" seems to be Unit instead of Array: does anyone know the solution?
Thank you
This is my entity and data class:
#Parcelize
#Entity(tableName = "user_table")
data class User(
#PrimaryKey(autoGenerate = true)
val id: Int,
val Name: String,
val StationCode: Int
): Parcelable
This is my Dao:
#Dao
interface UserDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addUser(user: User)
#Query("SELECT intArrayOf(StationCode) FROM user_table")
suspend fun stationlist() : Array
}
This is my database:
#Database(entities = [User::class], version = 1, exportSchema = false)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
#Volatile
private var INSTANCE: UserDatabase? = null
fun getDatabase(context: Context): UserDatabase{
val tempInstance = INSTANCE
if(tempInstance != null){
return tempInstance
}
synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
This is my Repository:
class UserRepository(private val userDao: UserDao) {
val readAllData: LiveData<List<User>> = userDao.readAllData()
suspend fun addUser(user: User){
userDao.addUser(user)
}
suspend fun stationlist(){
userDao.stationlist()
}
}
and this is my Viewmodel:
class UserViewModel(application: Application): AndroidViewModel(application) {
val readAllData: LiveData<List<User>>
private val repository: UserRepository
init {
val userDao = UserDatabase.getDatabase(
application
).userDao()
repository = UserRepository(userDao)
readAllData = repository.readAllData
}
fun addUser(user: User){
viewModelScope.launch(Dispatchers.IO) {
repository.addUser(user)
}
}
fun stationlist(){
viewModelScope.launch(Dispatchers.IO) {
repository.stationlist()
}
}
}
At last this is a part of the fragment:
class AddFragment : Fragment(), OnMapReadyCallback {
private lateinit var mUserViewModel: UserViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// here is all the part regarding displaying the layout and collecting data
mUserViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
view.add_btn.setOnClickListener {
insertDataToDatabase()
}
return view
}
private fun insertDataToDatabase() {
if(inputCheck(name, stationcode)){
val user = User(
0,
name,
stationcode)
mUserViewModel.addUser(user)
// Here is the problem:
val StationList= mUserViewModel.stationlist()
}
The problem is the dao method. Change it with this:
#Query("SELECT StationCode FROM user_table")
suspend fun stationlist() : Array<Int>
EDIT
There are some other problems with coroutines.
In fragment you call this:
val StationList= mUserViewModel.stationlist()
But this method does not return immediately.
fun stationlist(){
viewModelScope.launch(Dispatchers.IO) {
repository.stationlist()
}
}
You should implement a live data array, which is updated when async db query returns. In this way:
val stationList: MutableLiveData<Array<Int>>() = MutableLiveData()
fun stationlist(){
viewModelScope.launch(Dispatchers.IO) {
stationList.value = repository.stationlist()
}
}
And in fragment you have to observe the list:
mUserViewModel.stationList.observe(viewLifecycleOwner) { array ->
// do stuff
}
You also have to change repository stationlist() return type:
suspend fun stationlist(): Array<Int> {
return userDao.stationlist()
}
These are useful links to understand coroutines, room and livedata:
https://developer.android.com/topic/libraries/architecture/livedata
https://developer.android.com/codelabs/kotlin-android-training-coroutines-and-room#0
i am implementing Room with a vIewModel, my structure is the following
#DAO,#Entity,#Database,#Repository
#Entity(tableName="dx_table")
class dx_table(
#ColumnInfo(name = "name")
val naxme: String,
#PrimaryKey
#ColumnInfo(name = "phone")
val phone: String,
#ColumnInfo(name = "passx")
val passx: String,
#ColumnInfo(name = "login_fx")
val login_fx: String
)
#Dao
interface dx_dao{
#Query("SELECT * FROM dx_table")
fun get_all():LiveData<List<dx_table>>
#Insert
suspend fun insertTrx(dx_table:dx_table)
#Query("UPDATE dx_table SET login_fx =:login_fx where phone=:phonex")
suspend fun insertFx(login_fx: String,phonex: String)
#Query("SELECT * from dx_table where phone=:phonex")
suspend fun get_name_px(phonex: String):List<dx_table>
#Query("Delete from dx_table")
suspend fun deleteAll()
#Query("Select * from dx_table where login_fx=1")
suspend fun selectFx():List<dx_table>
}
#Database(entities = arrayOf(dx_table::class), version = 1, exportSchema = false)
public abstract class DxDatabase : RoomDatabase() {
abstract fun dxDao(): dx_dao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
#Volatile
private var INSTANCE: DxDatabase? = null
fun getDatabase(context: Context): DxDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
DxDatabase::class.java,
"dx_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
class dxRepository(private val dxDao: dx_dao ){
val k_d:LiveData<List<dx_table>> = dxDao.get_all()
suspend fun insert_trx(dx_table: dx_table){
dxDao.insertTrx(dx_table)
}
suspend fun insert_fx(login_fx: String,phonex: String) {
dxDao.insertFx(login_fx,phonex)
}
suspend fun select_fx() {
dxDao.selectFx()
}
suspend fun get_name_px(phonex: String) :List<dx_table> {
return dxDao.get_name_px(phonex) as List<dx_table>
}
}
The viewmodel is
class DxViewModel (application: Application) : AndroidViewModel(application) {
var repository: dxRepository
var k_d: LiveData<List<dx_table>>
init {
// Gets reference to WordDao from WordRoomDatabase to construct
// the correct WordRepository.
val dxDao = DxDatabase.getDatabase(application).dxDao()
repository = dxRepository(dxDao)
k_d = repository.k_d
}
fun insert_trx(dxTable: dx_table) = viewModelScope.launch {
repository.insert_trx(dxTable)
}
fun insert_fx(login_fx: String, phonex: String) = viewModelScope.launch {
repository.insert_fx(login_fx, phonex)
}
fun select_fx() = viewModelScope.launch {
repository.select_fx()
}
fun get_name_px(phonex: String) = viewModelScope.launch {
repository.get_name_px(phonex)
}
}
I can track the live data using observe,its not an issue, the problem i am facing is with the get_name_px(phone)
var mView = ViewModelProviders.of(this).get(DxViewModel::class.java)
var lm = mView.get_name_px(phone)
here lm seems to be job type , i need the return List , how do i get it .
In your viewModel function select_fx() return a job, because launch does not return result, so you have to either:
1) Use async and await
fun get_name_px(phonex: String) = viewModelScope.async {
repository.get_name_px(phonex)
}.await()
2) Not use launch viewModel, use it in Activity/Fragment
suspend fun get_name_px(phonex: String) = repository.get_name_px(phonex)
class CardFragment : Fragment() {
fun get() {
// launch in Dispatchers.Main
lifecycleScope.launch {
var lm = mView.get_name_px(phone)
}
// launch in background thread
lifecycleScope.launch(Dispatchers.Default) {
var lm = mView.get_name_px(phone)
}
}
}