Database_Impl doesn't exist - android app with Room - android

I'm trying to use Room. I get this error (in runtime):
java.lang.RuntimeException: cannot find implementation for
com.easythings.booky.database.BookyDatabase. BookyDatabase_Impl does
not exist
My BookyDatabase class:
#Database(entities = [Book::class, Chapter::class], version = 1)
abstract class BookyDatabase : RoomDatabase() {
abstract val bookDao: BookDao
abstract val chapterDao: ChapterDao
companion object {
#Volatile
private var _databaseInstance: BookyDatabase? = null
fun getDatabase(context: Context): BookyDatabase {
if (_databaseInstance == null)
synchronized(BookyDatabase::class.java) {
if (_databaseInstance == null)
_databaseInstance = Room.databaseBuilder(
context.applicationContext,
BookyDatabase::class.java,
"booky_database"
)
.fallbackToDestructiveMigration()
.build()
}
return _databaseInstance!!
}
}
}
I don't understand what is wrong.

The BookyDatabase_Impl most likely isn't being generated ...
a) because you lack the annotations.
b) because you lack an annotation processor.
see Database or Defining data using Room entities.

Related

Room.databaseBuilder not running in Android Jetpack

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) :-

Property delegate must have a 'getValue(InventoryApplication, KProperty<*>)' method. None of the following functions is suitable:

I stuck at this problem
class InventoryApplication : Application() {
val database: ItemDatabase by lazy { ItemDatabase.getDatabase(this) }
}
Error class
Code image
I had the same issue, i was able to resolve it by going to the ItemRoomDatabase.kt file, change the INSTANCE variable in the companion object, to return a non-null type ItemRoomDatabase? and the getDataBase() function to return a type ItemRoomDatabase, rather than a RoomDatabase return type as requested in the codelab example.
Your final code in the ItemRoomDatabase.kt file should look something like this:
#Database(entities = [Item::class], version = 1, exportSchema = false)
abstract class ItemRoomDatabase : RoomDatabase() {
abstract fun itemDao(): ItemDao
companion object {
#Volatile
private var INSTANCE: ItemRoomDatabase? = null
fun getDatabase(context: Context): ItemRoomDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
ItemRoomDatabase::class.java,
"item_database"
)
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
instance
}
}
}
}

How to init and insert in Room database using Kotlin?

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()

AppDatabase_Impl is not abstract and does not override abstract method appDao() in AppDatabase

I am new to android development and in my app I am using room database to make entry into database .
But i am getting this error in appManager_impl class
AppDatabase_Impl is not abstract and does not override abstract method appDao() in AppDatabase
public final class AppDatabase_Impl extends AppDatabase
My app manager class looks like this --
#Database(entities = [Data::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun appDao() : AppDao
companion object {
#Volatile
private var INSTANCE: AppDatabase? = null
fun getAppDatabase(context: Context, name : String): AppDatabase? {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java, name
).build()
}
return INSTANCE
}
fun destroyInstance() {
INSTANCE = null
}
}
}
The dependencies that I have used:
def roomDatabaseVersion = '2.3.0-alpha02'
implementation "androidx.room:room-ktx:$roomDatabaseVersion"
implementation "androidx.room:room-runtime:$roomDatabaseVersion"
kapt "androidx.room:room-compiler:$roomDatabaseVersion"
So, please aware to use kapt also.

Using room as singleton in kotlin

I'm trying to use Room as singleton so I didn't have to invoke Room.databaseBuilder() -which is expensive- more than once.
#Database(entities = arrayOf(
Price::class,
StationOrder::class,
TicketPrice::class,
Train::class,
TrainCategory::class
), version = 2)
#TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun dao(): TrainDao
companion object {
fun createDatabase(context: Context): AppDatabase
= Room.databaseBuilder(context, AppDatabase::class.java, "trains.db").build()
}
}
Note:
Can't use Object because Room requires using abstract class.
singleton must be thread safe because multiple threads might access it at the same time.
must be able to take Context as an argument.
I have looked at all similar StackOverflow questions and none of them satisfy my requirements
Singleton with argument in Kotlin isn't thread-safe
Kotlin - Best way to convert Singleton DatabaseController in Android isn't thread-safe
Kotlin thread save native lazy singleton with parameter uses object
After some research, I found that I have two options.
Double-checked locking
Initialization-on-demand holder idiom
I considered implementing one of them, but this didn't felt right for Kotlin - too much boilerplate code.
After more research, I stumbled upon this great article which provides an excellent solution, which uses Double-checked locking but in an elegant way.
companion object : SingletonHolder<AppDatabase, Context>({
Room.databaseBuilder(it.applicationContext, AppDatabase::class.java, "train.db").build()
})
From the article:
A reusable Kotlin implementation:
We can encapsulate the logic to
lazily create and initialize a singleton with argument inside a
SingletonHolder class. In order to make that logic thread-safe, we
need to implement a synchronized algorithm and the most efficient
one — which is also the hardest to get right — is the double-checked
locking algorithm.
open class SingletonHolder<T, A>(creator: (A) -> T) {
private var creator: ((A) -> T)? = creator
#Volatile private var instance: T? = null
fun getInstance(arg: A): T {
val i = instance
if (i != null) {
return i
}
return synchronized(this) {
val i2 = instance
if (i2 != null) {
i2
} else {
val created = creator!!(arg)
instance = created
creator = null
created
}
}
}
}
Extra:
if you want Singleton with two arguments
open class SingletonHolder2<out T, in A, in B>(creator: (A, B) -> T) {
private var creator: ((A, B) -> T)? = creator
#Volatile private var instance: T? = null
fun getInstance(arg0: A, arg1: B): T {
val i = instance
if (i != null) return i
return synchronized(this) {
val i2 = instance
if (i2 != null) {
i2
} else {
val created = creator!!(arg0, arg1)
instance = created
creator = null
created
}
}
}
}
In this particular case I would resort to using Dagger 2, or some other dependency injection library like Koin or Toothpick. All three libraries allow to provide dependancies as singletons.
Here's the code for Dagger 2 module:
#Module
class AppModule constructor(private val context: Context) {
#Provides
#Singleton
fun providesDatabase(): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"train.db")
.build()
}
}
AppComponent:
#Singleton
#Component(modules = arrayOf(
AppModule::class
))
interface AppComponent {
fun inject(viewModel: YourViewModel)
fun inject(repository: YourRepository)
}
Application class to provide injection:
class App : Application() {
companion object {
private lateinit var appComponent: AppComponent
val component: AppComponent get() = appComponent
}
override fun onCreate() {
super.onCreate()
initializeDagger()
}
private fun initializeDagger() {
component = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
}
And then inject your database as singleton to wherever you need it (for example in your app's repository):
#Inject lateinit var appDatabase: AppDatabase
init {
App.component.inject(this)
}
Used #Volatile for thread safety.
public abstract class AppDatabase : RoomDatabase() {
abstract fun trainDao(): trainDao
companion object {
#Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): Db = INSTANCE ?: synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase ::class.java,
"train-db"
).build()
INSTANCE = instance
instance
}
}
}
taken from : https://developer.android.com/codelabs/android-room-with-a-view-kotlin#7
You could make use of the Kotlin standard library's
fun <T> lazy(LazyThreadSafetyMode.SYNCHRONIZED, initializer: () -> T): Lazy<T>
companion object {
private lateinit var context: Context
private val database: AppDatabase by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
Room.databaseBuilder(context, AppDatabase::class.java, "trains.db").build()
}
fun getDatabase(context: Context): AppDatabase {
this.context = context.applicationContext
return database
}
}
Personally though, I would normally add ApplicationContext-dependent singletons inside the Application, e.g.
<!-- AndroidManifest.xml -->
<manifest>
<application android:name="MyApplication">
...
class MyApplication : Application() {
val database: AppDatabase by lazy {
Room.databaseBuilder(this, AppDatabase::class.java, "train.db").build()
}
}
You can even define an extension method for easy access as context.database.
val Context.database
get() =
generateSequence(applicationContext) {
(it as? ContextWrapper)?.baseContext
}.filterIsInstance<MyApplication>().first().database
Here's how i figured out...
#Database(entities = [MyEntity::class], version = dbVersion, exportSchema = true)
abstract class AppDB : RoomDatabase() {
// First create a companion object with getInstance method
companion object {
fun getInstance(context: Context): AppDB =
Room.databaseBuilder(context.applicationContext, AppDB::class.java, dbName).build()
}
abstract fun getMyEntityDao(): MyEntityDao
}
// This is the Singleton class that holds the AppDB instance
// which make the AppDB singleton indirectly
// Get the AppDB instance via AppDBProvider through out the app
object AppDBProvider {
private var AppDB: AppDB? = null
fun getInstance(context: Context): AppDB {
if (appDB == null) {
appDB = AppDB.getInstance(context)
}
return appDB!!
}
}
singleton in kotlin is real easy just do this
companion object {
#JvmStatic
val DATABASE_NAME = "DataBase"
#JvmField
val database = Room.databaseBuilder(App.context(), DataBase::class.java, DataBase.DATABASE_NAME).build()
}

Categories

Resources