create a custom function with body in dao room database - android

I want to create a DAO Object with a custom function like this
#Dao
interface DataAccessObjDao{
#Insert
fun insert(someEntity: SomeEntity)
#Ignore
fun sampleFun(){
insert(SumEntity())
}
}
but compiler complains about sample fun
Class 'DataAccessObjDao_Impl' must either be declared abstract or implement abstract method 'sampleFun()' in 'DataAccessObjDao

#Ignore is for property or entity, can not be used on a method.
You can do this by extending your interface :
fun DataAccessObjDao.sampleFun(){
// irrelevant code
}
or by adding #Transaction
#Transaction
fun sampleFun(){
firstDelete()
thenInsert()
}

As you wrote, Dao can also be abstract class. You can define a method with body inside abstract class. However Dao is for querying tables and have different Dao objects for different tables. It would be better if they had only insert, delete, update and select queries. I also BaseDao to minimize Dao code.
You should call dao.insert(SumEntity()) inside a LocalDataSource class which you use Repository like in this guide.

Related

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?

How is it possible to call without implementing an abstract method in this Android's DB code?

I'm studying Android and I'm new to DB. I am trying to use the Room library to use the DB.
I'm looking at the sample code, but there's something I don't understand.
It is to call the abstract method of the AppDatabase abstract class without implementing it.
At least as far as I know, abstract classes cannot be instantiated.
But I'm curious how it can be called and how to use the returned value.
(Same for Kotlin.)
Am I wrong about JAVA or Kotlin?
ToDoDao.interface
#Dao // Data Access Object
interface ToDoDao {
#Query("SELECT * FROM ToDo")
abstract fun getAll(): List<ToDo>
#Insert
void insert(ToDo todo)
#Update
void update(ToDo todo)
#Delete
void delete(ToDo todo)
}
AppDatabase.class
#Database(entities = [Todo.class], version = 1)
abstract class AppDatabase extends RoomDatabase {
public abstract void TodoDao todotDao();
}
Main.class
AppDatabase db = Room.databaseBuilder(this, AppDatabase.class, "todo-db").build();
mResultTextView.setText(db.todoDao().getAll().toString); // THIS
We don't build database like this for simplicity , this and that are an example you can refer for building database and for accessing it use
val DB = AppDatabase.getInstance(context).ToDoDao ()

How to inject Room in viewmodel using koin

This is my first mvvm project with koin and I'm using Room database. I'm making network calls in viemwodel, and after fetching data from the api I want to store it in the database. Below is my class which has all the db methods like insert and delete.
class UserViewModel(application: Application) : AndroidViewModel(application) {
private val userSDAO: UserDAO
private val userDB: AppSettingsDatabase
init {
userDB = AppSettingsDatabase.getAppSettingsDatabase(application.applicationContext)!!
userDAO = userDB.userDao()
}
fun getAppSetting():LiveData<AppSettingsEntity>{
return userDB.appSettingDao().getAllAppSettings()
}
fun deleteUser() {
userDB.databaseWriteExecutor.execute { ->
userDAO.deleteUser()
}
}
}
I was calling this class from activity like this
userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
now I want to call this inside a viewmodel class where I am making the network api calls and I don't know what is the best approach to access it using koin, or any other way.I have different methods where I need database access and I have to initialize it like above in every method.
class SubscriptionViewModel(): BaseViewModel() {
fun init(owner:ViewModelStoreOwner) {
userServiceViewModel = ViewModelProvider(owner).get(UserServiceViewModel::class.java)
}
}
In general, it's a better pattern to not accessing a db object in ViewModel. I mean the dao should be used in a data source class, then the data source would be injected in the ViewModel or even better, use the data source in a repository, then inject the repository in the ViewModel.
After that, you must not access a ViewModel inside another one. They should be independent. If you want to do something with the db or api in multiple ViewModels, access to them through a common repository class.
Please take a look at: https://developer.android.com/jetpack/guide#overview

Room: Conflicting Declarations

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

Room DAO with inherited interfaces

I have a DAO interface, of which I have multiple implementations and I want one of them to be a Room implementation (Kotlin):
interface BaseDao {
fun getAll(): Single<List<SomeData>>
fun insert(data: List<SomeData>)
}
and my Room (RxRoom) interface:
#Dao
interface RxRoomBaseDao: BaseDao {
#Query("SELECT * FROM some_data")
override fun getAll(): Single<List<SomeData>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
override fun insert(data: List<SomeData>)
}
It looks like the Room compiler tries to compile the BaseDao instead of the RxRoomBaseDao and complains error: Dao class must be annotated with #Dao and for both methods error: A DAO method can be annotated with only one of the following:Insert,Delete,Query,Update.
I have also tried an abstract class for the RxRoomBaseDao with the same result.
Is this a known bug or limitation of the Room compiler? How can I achieve what I want without polluting my BaseDao with Room's annotations?
It seems OP has since moved on from Room, but fwiw, it's possible to use generics to solve this problem:
interface BaseDao<T> {
#Insert
fun insert(vararg obj: T)
}
#Dao
abstract class DataDao : BaseDao<Data>() {
#Query("SELECT * FROM Data")
abstract fun getData(): List<Data>
}
Check out the "Use DAO's inheritance capability" section in this Official Room blog post
Create your dao as an abstract class:
#Dao
public abstract class EntityDao implements Base {
#Override
#Query("SELECT * FROM " + Entity.TABLE_NAME)
public abstract Flowable<List<Entity>> getEntities();
}
interface Base {
Flowable<List<Entity>> getEntities();
}

Categories

Resources