I'm new to kotlin and room, after following an official android guide i ended up by setting my entities, my DAO and my Database,
the issue is i can't understand on how can i use the function from dao in my fragment...
So my Database looks like this:
#Database(entities = [Articolo::class], version = 1, exportSchema = false)
abstract class ArticoliDatabase: RoomDatabase() {
abstract val articoliDao: ArticoliDAO
companion object {
#Volatile
private var INSTANCE: ArticoliDatabase? = null
fun getInstance(context: Context): ArticoliDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
ArticoliDatabase::class.java,
"pdt_database"
)
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
}
return instance
}
}
}
}
Then in my fragment i've done the following:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
db = ArticoliDatabase.getInstance(requireContext())
}
And in the same fragment in my click function i'm doing the following to insert
db.articoliDao.insert(Articolo(barcode, qta))
But the app even doesn't build correctly by saying that
ArticoliDatabase_Impl does not exist
So what is the right way to initialize and use the room database with kotlin?
I just need to simply insert and show the data from the db in a listview that's it..
The problem is in the build.gradle file. Can you please check if your import of room library looks like:
annotationProcessor "androidx.room:room-compiler:$room"
Since you are using kotlin, you must use kapt for annotation processor dependencies
This is how your room dependencies should look like
implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-ktx:$room"
kapt "androidx.room:room-compiler:$room"
Create Database class AppDatabase and Dao Interface
AppDatabase
#Database(
entities = [User::class, Quote::class],
version = 1
)
abstract class AppDatabase : RoomDatabase() {
abstract fun getUserDao(): UserDao
abstract fun getQuoteDao(): QuoteDao
companion object {
#Volatile
private var instance: AppDatabase? = null
private val LOCK = Any()
operator fun invoke(context: Context) = instance ?: synchronized(LOCK) {
instance ?: buildDatabase(context).also {
instance = it
}
}
private fun buildDatabase(context: Context) =
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"MyDatabase.db"
).build()
}
}
Dao Interface Here
#Dao
interface UserDao{
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsert(user: User) : Long
#Query("SELECT * FROM user WHERE uid = $CURRENT_USER_ID")
fun getuser() : LiveData<User>
}
Now in your fragment or activity you can insert and get user data by just craeting an object of dabase class as given below
private val db: AppDatabase
db=AppDatabase(context)
fun saveUser(user: User) = db.getUserDao().upsert(user)
fun getUser() = db.getUserDao().getuser()
Related
In my project, I use the Singleton design pattern, add a companion object and a function that returns a database object instance. This will avoid creating multiple instances
database object through which the connection to the SQL server is established.
I have the following code to connect to the database:
// Annotates class to be a Room Database with a table (entity) of the Word class
#Database(entities = arrayOf(ShoppingList::class), version = 1, exportSchema = false)
public abstract class ShoppingListRoomDatabase : RoomDatabase() {
abstract fun shoppingListDao(): ShoppingListDao
companion object {
// Singleton prevents multiple instances of database opening at the
// same time.
#Volatile
public var INSTANCE: ShoppingListRoomDatabase? = null
fun getDatabase(context: Context, scope: CoroutineScope): ShoppingListRoomDatabase {
// 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,
ShoppingListRoomDatabase::class.java,
"shopping_list_database"
).addCallback(ShoppingListDatabaseCallback(scope)).build()
INSTANCE = instance
// return instance
instance
}
}
}
}
private class ShoppingListDatabaseCallback(
private val scope: CoroutineScope
) : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
ShoppingListRoomDatabase.INSTANCE?.let { database ->
scope.launch {
populateDatabase(database.shoppingListDao())
}
}
}
fun populateDatabase(shoppingListDao: ShoppingListDao) {
shoppingListDao.deleteAll()
var shoppingList = ShoppingList(1,"First List")
shoppingListDao.insert(shoppingList)
shoppingList = ShoppingList(2, "Second List!")
shoppingListDao.insert(shoppingList)
}
}
Interface:
#Dao
interface ShoppingListDao {
#Query("SELECT * FROM shopping_lists ORDER BY id ASC")
fun getOrderedShoppingLists(): Flow<List<ShoppingList>>
#Insert
fun insert(shoppingList: ShoppingList)
#Query("DELETE FROM shopping_lists")
fun deleteAll()
}
How can I get this database instance in another kotlin class to work with it?
I'm learning how to implement Room from Android jetpack into my android studio app but a message saying "not enough information to infer type variable T" keeps showing up when I try to use Room.databaseBuilder
this is my code:
// if there is no instance, we create a new one
synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user_database"
).build()
INSTANCE = instance
return instance
}
I can add the surrounding function or the whole file if that helps. Thanks in advance!
I'm not sure but this is the problem i faced as well and i solved it by downgrading 'implementation 'androidx.core:core-ktx:1.6.0' to 'implementation 'androidx.core:core-ktx:1.5.0' in the build.gradle file
If you have for example:-
#Database(entities = [Objects::class], version = 1)
abstract class MyDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDAO
companion object {
var INSTANCE: MyDatabase? = null
fun getInstance(context: Context): MyDatabase {
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
The you get the not enough information to infer type variable T e.g. :-
Changing to use the same class then all is fine, e.g.
#Database(entities = [Objects::class], version = 1)
abstract class UserDatabase: RoomDatabase() { //<<<<<<<<<< CHANGED
abstract fun getAllDao(): AllDAO
companion object {
var INSTANCE: UserDatabase? = null //<<<<<<<<<< CHANGED
fun getInstance(context: Context): UserDatabase { //<<<<<<<<<< CHANGED
if (INSTANCE == null) {
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java, //<<<<<<<<<< AS WAS/ MATCHES
"user_database"
)
.allowMainThreadQueries() /*<<<<<<<<<< ADDED FOR convenience brevity */
.build()
INSTANCE = instance
return instance
}
}
return INSTANCE!!
}
}
}
Note run on the main thread to just demonstrate that the above runs (see following example run)
As a test/proof consider :-
class MainActivity : AppCompatActivity() {
lateinit var db1: UserDatabase
lateinit var dao1: AllDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db1 = UserDatabase.getInstance(this) //<<<<<<<<<< GET INSTANCE
dao1 = db1.getAllDao()
dao1.getObjects(listOf("nothing to get as no data has been added, just forcing open/create of database")) //<<<<<<<<< as per text USE/OPEN/CREATE Database(first use, once created database is opened as it persists)
// existing app used ....
The result (ignore TheDatabase as existing code was used for the answer) :-
I have an application where i need to prepopulate my database when it is created , i'm using dagger hilt to inject and provide dependencies ( room daos ) , when i try to insert data , it asks for movie database but don't know how to get its reference inside appmodule, thank you for any help in advance.
This is my database :
// this is my database
#Database(entities = [DataModel::class,MovieResultItem::class], version = 1, exportSchema = false)
abstract class MoviesDatabase : RoomDatabase() {
abstract fun popularDao() : PopularMoviesDao
}
Providing dao as dependency
#Singleton
#Provides
fun providePopularMoviesDao(moviesDatabase: MoviesDatabase) : PopularMoviesDao {
return moviesDatabase.popularDao()
}
Providing database instance
#Singleton
#Provides
fun provideDatabase(#ApplicationContext context: Context): MoviesDatabase {
return Room.databaseBuilder(context.applicationContext,
MoviesDatabase::class.java, "movie.db")
.addCallback(object : RoomDatabase.Callback(){
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// here it asks for database instance , not sure how to get it
providePopularMoviesDao().insertPopularMovies(getMovieResultItem())
}
})
.fallbackToDestructiveMigration()
.build()
}
#Database(entities = [DataModel::class,MovieResultItem::class], version = 1, exportSchema = false)
abstract class MoviesDatabase : RoomDatabase() {
abstract fun popularDao(): PopularMoviesDao
#Volatile
private var INSTANCE: MoviesDatabase? = null
#Singleton
#Provides
fun provideDatabase(#ApplicationContext context: Context): MoviesDatabase {
return INSTANCE ?: synchronized(this) {
val databaseInstance = Room.databaseBuilder(
context.applicationContext,
MoviesDatabase::class.java, "movie.db"
)
.fallbackToDestructiveMigration()
.addCallback(InsertDatabaseCallback())
.build()
INSTANCE = databaseInstance
return instance
private class InsertDatabaseCallback() :RoomDatabase.Callback() {
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
INSTANCE?.let {
it.providePopularMoviesDao().insertPopularMovies(getMovieResultItem())
}
}
}
}
}
}
Would you please use the above code in your class MoviesDatabase.
Here I have added an INSTANCE variable for database and it is marked as Volatile.
Volatile means, it will not be stored in the local cache. There are lot of articles you can explore for more information on Volatile.
I have modified the provideDatabase function, so that the database instance is stored in variable INSTANCE
Then you can use this INSTANCE wherever required.
Also, I have separated the callback into class InsertDatabaseCallback for simpilicity and readability. Here you can use the database INSTANCE to perform your operation providePopularMoviesDao().insertPopularMovies(getMovieResultItem())
NOTE: The code may show syntax error or curly braces error, depending on your setup and imports. Do not worry there, please make slight changes OR add curly brackets (if required) and it should work fine.
I am writing an android application following the Android Architectural Components design.
This is the database class:
#Database(entities = [Authentication::class],version = 1, exportSchema = false)
abstract class AuthDB: RoomDatabase(){
abstract val authenticationDao :AuthenticationAccessObject
companion object{
#Volatile
private var INSTANCE: AuthDB? = null
fun getInstance(context: Context): AuthDB {
synchronized(this){
var instance = INSTANCE
if(instance == null){
instance = Room.databaseBuilder(
context.applicationContext,
AuthDB::class.java,
"authentication_database"
)
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
}
return instance
}
}
}
}
This is the Repository class:
class Repository2() {
private val database: AuthDB = AuthDB.getInstance(context = getContext())
private val daoA = database.authenticationDao
//Function to register a new user to system
fun insertAuth(userData: Any){
if (userData is Authentication){
daoA.insertAuth(userData)
} else {
throw IllegalArgumentException()
}
}
My target is that when I write the ViewModel, I want to create instance of Repository2 and call functions for example as follows:
var repo = Repository2()
repo.insertAuth(authenticationObject)
I am having problem giving context to getInstance in the Repository. The context should be such that when I instantiate the repository, it should automatically get the application context and instantiate the AuthDB database.
Until now,
I have tried to create Application class that extends Application and tried to get application context from there as suggested in another stackoverflow solution
Instantiated database with following code and failed:
private val database: AuthDB = AuthDB.getInstance(context = getContext())
Instantiated database with following code and failed:
private val database: AuthDB = AuthDB.getInstance(Application.getApplicationContext())
I have been trying for about two days now and nothing is working, I believe I am missing a major concept here. I hope someone can nudge me in the right direction?
Kind regards,
Salik
try this solution
EDIT:-
use this way to your RoomDatabase
#Database(
entities = [CompaniesModel::class, CompaniesHomeModel::class, UserPoint::class, Image::class, Offer::class, Rewords::class, BranchModel::class, PointsModel::class, RedeemModel::class, MainData::class, SubData::class],
version = 15)
abstract class DataBase : RoomDatabase() {
abstract fun homeDao(): HomeDao
abstract fun menuDao(): MenuDao
abstract fun companiesDao(): CompaniesListDao
abstract fun branchesDao(): BranchesDao
companion object {
#Volatile
private var databaseInstance: DataBase? = null
fun getDatabaseInstance(mContext: Context): DataBase =
databaseInstance ?: synchronized(this) {
databaseInstance ?: buildDatabaseInstance(mContext).also {
databaseInstance = it
}
}
private fun buildDatabaseInstance(mContext: Context) =
Room.databaseBuilder(mContext, DataBase::class.java, "crm")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build()
}
}
and for the getContext(), use Application() class like this:
class App : Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: App
}
}
and pass it like this
private val database: AuthDB = AuthDB.getInstance(app.instance)
I am writing my first Android application in Kotlin.
I was wondering how to instantiate a Room Persistence database so that I can access it from many different activities.
Sorry if my question is dumb and not too specific, but I'm not sure what details may be useful.
Try this way..
make app level activity like below for database creation.
class AppActivity:Application() {
companion object {
var db: AppDatabase? = null
fun getDatabase(): AppDatabase? {
return db
}
}
override fun onCreate() {
super.onCreate()
db= Room.databaseBuilder(applicationContext, AppDatabase::class.java,"DB").allowMainThreadQueries().build()
}
}
after that this activity define in manifest in application tag..
android:name=".app.AppActivity"
after that make dao class..
#Dao
interface TodoDao {
#Query("SELECT * FROM Todo")
fun getTodoData()= mutableListOf<Todo>()
#Insert
fun insertTodo(todo: Todo)
#Update
fun updateTodo(todo: Todo)
#Delete
fun deleteTodo(todo: Todo)
}
make database class for access all dao class..
#Database(entities = arrayOf(User::class,Assignment::class,Todo::class, Student::class,Event::class,Comment::class,Feedback::class ,Achivement::class,Note::class, Syllabus::class, Education::class, Mark::class, UserWork::class, Exam::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getUser(): UserDao
abstract fun getStudent(): StudentDao
abstract fun getEducationDao(): EducationDao
abstract fun getWorkDetails(): UserWorkDao
abstract fun getExamDao(): ExamDao
abstract fun getMarkDao(): MarkDao
abstract fun getSyllabusDao(): SyllabusDao
abstract fun getNoteDao(): NoteDao
abstract fun getAchivement():AchivementDao
abstract fun getFeedbackDao():FeedbackDao
abstract fun getCommentDao():CommentDao
abstract fun getEventDao():EventDao
abstract fun getAssignDao():AssignmentDao
abstract fun getTodoDao():TodoDao
}
make table for todo..
#Entity
class Todo {
#PrimaryKey(autoGenerate = true)
var id:Int=0
var time:String=""
var activityName:String=""
var hours:String=""
var types:String=""
}
after in activity or fragment insert data like below..
var data=Todo().apply {
time=mSpTime?.selectedItem.toString()
types=mSpTypes?.selectedItem.toString()
activityName=mEtAname?.text.toString().trim()
hours=mEtHour?.text.toString().trim()
}
AppActivity.getDatabase()?.getTodoDao()?.insertTodo(data)
You can create instance using companion objectlike this,
#Database(entities = arrayOf(UserEntity::class), version = 1)
abstract class UserDb : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
private var INSTANCE: UserDb? = null
fun getInstance(context: Context): UserDb? {
if (INSTANCE == null) {
synchronized(UserDb::class) {
INSTANCE = Room.databaseBuilder(context.applicationContext, UserDb::class.java, "user.db").build()
}
}
return INSTANCE
}
fun destroyInstance() {
INSTANCE = null
}
}
}
You can get instance of db in your activity like this,
UserDb.getInstance(this)
Alternatively, you can also use dependency injection libraries like Dagger
Check the documentation in this link, here you will find detailed step by step tutorial about setting up Room.
The correct way to have an instance of the DB is:
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
Remember you have to use one more dependency for kotlin and room
// Room
implementation "android.arch.persistence.room:runtime:1.1.1"
annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.1"
apply plugin: "kotlin-android"
apply plugin: "kotlin-kapt"
apply plugin: "kotlin-android-extensions"