androidkotlinandroid-roomdagger-hiltkapt

"java.lang.RuntimeException: Cannot find implementation for Room database - AccountDataDataBase_Impl does not exist"


I'm encountering a java.lang.RuntimeException in my Android application, specifically:

java.lang.RuntimeException: Cannot find implementation for com.daviddev.passwordmanager.Room.AccountDataDataBase. AccountDataDataBase_Impl does not exist

This error suggests that Room is unable to locate the generated implementation for my database class, AccountDataDataBase. I've already tried cleaning and rebuilding my project, syncing Gradle, and ensuring my Room dependencies are correctly configured. However, the issue persists.I've made sure that my database class is abstract and extends RoomDatabase. It's annotated with @Database and includes the entities, version, and exportSchema properties. I have a DAO defined with appropriate annotations. My Room dependencies in build.gradle seem correct. The complete project is at: https://github.com/bdavidgm/test

@Database(entities = [AccountData::class, AccountName::class], version = 1, exportSchema = false)
abstract class AccountDataDataBase: RoomDatabase() {
    abstract fun accountDataDao() : AccountDataDatabaseDao
} 
class accountDataRepository @Inject constructor(private val accountDataDatabaseDao: AccountDataDatabaseDao) {

    suspend fun addAccountData(accountData: AccountData) = accountDataDatabaseDao.insert(accountData)
    suspend fun updateAccountData(accountData: AccountData) = accountDataDatabaseDao.update(accountData)
    suspend fun deleteAccountData(accountData: AccountData) = accountDataDatabaseDao.delete(accountData)
    fun getAllAccountData() = accountDataDatabaseDao.getAllAccountData().flowOn(Dispatchers.IO).conflate()
    fun getAccountDataById(id:Long): Flow<AccountData> = accountDataDatabaseDao.getAccountDataById(id).flowOn(Dispatchers.IO).conflate()
    suspend fun getLastAccountData(accountId: Long): Flow<AccountData> = accountDataDatabaseDao.getLastAccountData(accountId).flowOn(Dispatchers.IO).conflate()
    suspend fun getAllAccountData(accountId: Long): Flow<List<AccountData>> = accountDataDatabaseDao.getAllAccountData(accountId).flowOn(Dispatchers.IO).conflate()
}
@Dao // Data Access Observer
interface AccountDataDatabaseDao {

    // Crud
    @Query("SELECT * FROM AccountData")
    fun getAllAccountData(): Flow<List<AccountData>>

    @Query("SELECT * FROM AccountData WHERE id = :id")
    fun getAccountDataById(id: Long): Flow<AccountData>

    @Query("SELECT * FROM AccountData WHERE account_id = :accountId ORDER BY creation_date DESC LIMIT 1")
    suspend fun getLastAccountData(accountId: Long): Flow<AccountData>

    @Query("SELECT * FROM AccountData WHERE account_id = :accountId ORDER BY creation_date DESC")
    suspend fun getAllAccountData(accountId: Long): Flow<List<AccountData>>

   /* @Query("SELECT account_id FROM AccountName WHERE account_name = :accountName")
    suspend fun getAccountID(accountName: String): Long

    @Query("SELECT account_name FROM AccountName")
    suspend fun getAllAccountName(): flow<List<String>>*/

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(accountData: AccountData)

    @Update(onConflict = OnConflictStrategy.REPLACE)
    suspend fun update(accountData: AccountData)

    @Delete
    suspend fun delete(accountData: AccountData)

}

@Entity(tableName = "AccountData")
data class AccountData(
    @PrimaryKey(autoGenerate = true)
    val id : Long = 0,

    @ColumnInfo(name = "account_id")
    val account_id : Long =0,

    @ColumnInfo(name = "user_name")
    val user_name: String="",

    @ColumnInfo(name = "password")
    val password: String="",

    @ColumnInfo(name = "creation_date")
    val creation_date : LocalDateTime = LocalDateTime.now(),

    @ColumnInfo(name = "expiration_date")
    val expiration_date : LocalDateTime = LocalDateTime.now()
)

@Entity(tableName = "AccountName")
data class AccountName(
    @PrimaryKey(autoGenerate = true)
    val account_id : Long = 0,

    @ColumnInfo(name = "account_name")
    val account_name: String
)

Solution

  • So I checked and cloned the GitHub repo you mentioned and noticed that you're using:
    annotationProcessor("androidx.room:room-compiler:$room_version")

    in your Gradle build file. Since your project is Kotlin-based, you should replace that with:
    kapt("androidx.room:room-compiler:$room_version")

    **
    Note:** Using kapt might throw a warning like:
    w: Kapt currently doesn't support language version 2.0+. Falling back to 1.9.
    If you encounter that, you can consider using KSP (Kotlin Symbol Processing) as an alternative.

    Additionally, I noticed your entity class defines columns like this:

    @ColumnInfo(name = "creation_date")
    val creation_date: LocalDateTime = LocalDateTime.now(),
    @ColumnInfo(name = "expiration_date")
    val expiration_date: LocalDateTime = LocalDateTime.now(),
    

    Room doesn't know how to store LocalDateTime values by default, so you should use TypeConverters. Here's how you can do it:

    Create a Converter.kt:

    import androidx.room.TypeConverter
    import java.time.LocalDateTime
    
    class Converter {
        @TypeConverter
        fun fromDate(date: LocalDateTime): String {
            return date.toString()
        }
    
        @TypeConverter
        fun toDate(date: String): LocalDateTime {
            return LocalDateTime.parse(date)
        }
    }
    

    Update your database class:

    @Database(entities = [AccountData::class, AccountName::class], version = 1, exportSchema = false)
    @TypeConverters(Converter::class)
    abstract class AccountDataDataBase : RoomDatabase() {
        abstract fun accountDataDao(): AccountDataDatabaseDao
    }
    

    Additionally, In your DAO, you have these functions:

    // CRUD operations
    @Query("SELECT * FROM AccountData")
    fun getAllAccountData(): Flow<List<AccountData>>
    @Query("SELECT * FROM AccountData WHERE id = :id")
    fun getAccountDataById(id: Long): Flow<AccountData>
    @Query("SELECT * FROM AccountData WHERE account_id = :accountId ORDER BY creation_date DESC LIMIT 1")
    suspend fun getLastAccountData(accountId: Long): Flow<AccountData>
    @Query("SELECT * FROM AccountData WHERE account_id = :accountId ORDER BY creation_date DESC")
    suspend fun getAllAccountData(accountId: Long): Flow<List<AccountData>>
    

    You should remove suspend from the functions that return a Flow

    Why Remove suspend?

    Functions that return a Flow are already designed to work asynchronously and lazily. Marking them as suspend is unnecessary and may confuse the intended usage because:

    So, you should change the DAO functions that return Flow to be normal functions without the suspend modifier:

    @Query("SELECT * FROM AccountData WHERE account_id = :accountId ORDER BY creation_date DESC LIMIT 1")
    fun getLastAccountData(accountId: Long): Flow<AccountData>
    
    @Query("SELECT * FROM AccountData WHERE account_id = :accountId ORDER BY creation_date DESC")
    fun getAllAccountData(accountId: Long): Flow<List<AccountData>>
    

    Then in your repository, remove the suspend keyword from the functions that wrap these flow-returning DAO calls:

    class AccountDataRepository @Inject constructor(
        private val accountDataDatabaseDao: AccountDataDatabaseDao
    ) {
    
        suspend fun addAccountData(accountData: AccountData) = accountDataDatabaseDao.insert(accountData)
        suspend fun updateAccountData(accountData: AccountData) = accountDataDatabaseDao.update(accountData)
        suspend fun deleteAccountData(accountData: AccountData) = accountDataDatabaseDao.delete(accountData)
        
        fun getAllAccountData(): Flow<List<AccountData>> =
            accountDataDatabaseDao.getAllAccountData().flowOn(Dispatchers.IO).conflate()
    
        fun getAccountDataById(id: Long): Flow<AccountData> =
            accountDataDatabaseDao.getAccountDataById(id).flowOn(Dispatchers.IO).conflate()
    
        fun getLastAccountData(accountId: Long): Flow<AccountData> =
            accountDataDatabaseDao.getLastAccountData(accountId).flowOn(Dispatchers.IO).conflate()
    
        fun getAllAccountData(accountId: Long): Flow<List<AccountData>> =
            accountDataDatabaseDao.getAllAccountData(accountId).flowOn(Dispatchers.IO).conflate()
    }
    

    Reason for Removing suspend in Repository: