I have an existing Android project in which I have created a new Android Library (offlineservicelibrary)(File/new/New module/Android library). In this library I want to create a room database.
I followed https://developer.android.com/training/data-storage/room and created Entity (OfflineData) and DAO(OfflineDataDao) setup for my database, but I am a bit confused about where to setup AppDatabase and how Link it to the library.
#Database(entities = arrayOf(OfflineData::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getOfflineDataDao(): OfflineDataDao
}
val db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database-name"
).build()
In my Library I do not have any class which extends Application class. Should I be creating a new class which extends Application class and do the above steps there.
Please suggest on what I should be doing to get the Library setup with Room database please
This is my Project structure with Library
Make DatabaseManager Singleton class inside your module. Use #JvmStatic help you call from Java without Companion
class DatabaseManager private constructor(private val dp: AppDatabase) {
fun loadData() : List<OfflineData> {
return dp.getOfflineDataDao().loadAll() // I assume you already defined loadAll method in DAO
}
companion object {
#Volatile
private var INSTANCE: DatabaseManager? = null
#JvmStatic
fun getInstance(context: Context): DatabaseManager {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: run {
val db = Room.databaseBuilder(
context,
AppDatabase::class.java, "database-name"
).build()
DatabaseManager(db).also { INSTANCE = it }
}
}
}
}
}
In other modules you need to use your database, you have to implementation offlineservicelibrary in gradle and pass context to it when call
DatabaseManager.getInstance(context).loadData()
Related
I am trying to populate data from CSV to room database using kotlin in Android. I tried to convert the java code to Kotlin from this Populate Room database using csv file, but I am not able to access the Dao. Is there any better way to populate data from csv using Kotlin.
#Database(
entities = [MenuItem::class,
Order::class,
OrderItem::class,
Restaurant::class,
RestaurantRating::class,
User::class,
UserFavorite::class,
UserSearch::class],
version = 15,
exportSchema = false)
abstract class AppDatabase: RoomDatabase() {
abstract val orderDao: OrderDao
abstract val restaurantDao: RestaurantDao
abstract val userDao: UserDao
companion object{
#Volatile
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"foodie_database.db"
)
.fallbackToDestructiveMigration()
.addCallback(object : RoomDatabase.Callback(){
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
Executors.newSingleThreadExecutor().execute {
run {
}
}
}
})
.build()
INSTANCE = instance
}
return instance
}
}
}
}
From what I understand you are trying to pre-populate your database on first app startup. I didn't look into exactly how you can get access to the dao manually but the java example you pointed to I believe is using dagger dependency injection to get an instance of the dao.
How to overcome the circular dependency for a callback from Room.databaseBuilder using Dagger Hilt is wonderfully explained in a Code in flow video (link). I think this will give you enough information to figure out how to make the callback work.
Alternatively, and this is the one I prefer, you can use a database file instead of the csv file and use Room's inbuilt pre-population feature.
Just call .createFromAsset()orcreateFromFile() with Room.databaseBuilder()
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("myapp.db") //myapp.db in the assets folder
.build()
more from docs https://developer.android.com/training/data-storage/room/prepopulate#kotlin
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'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()
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)
Room persistence library version 2.2.0-alpha01 has added the ability to use pre-packaged databases.
https://developer.android.com/jetpack/androidx/releases/room
Can someone provide an example for how to initialize the room database builder?
I'm using this :
#Database(entities = [Users::class], version = 1, exportSchema = false)
abstract class AppDataBase : RoomDatabase() {
companion object {
private const val DATABASE_NAME = "you_name"
private const val DATABASE_DIR = "database/you_name.db" // Asset/database/you_name.db
fun getInstance(context: Context): AppDataBase {
return Room
.databaseBuilder(context, AppDataBase::class.java, DATABASE_NAME)
.createFromAsset(DATABASE_DIR)
.build()
}
}
abstract fun getUsers(): UsersDao
}
for more information refer here
If you need update DB from Asset!
1. You need level up version Database in settings Room!
2. Add .fallbackToDestructiveMigration() method in getInstance
3. And need level up version in you db file;