Hamza
Hamza

Reputation: 801

How to read a value from Room database via View Model, Repository and DAO?

I'm developing an Android app using Room database. I've already learnt to write values to database. As far as reading/fetching is concerned, I could get help only related to fetching whole list of values form the database (and populate them in a RecyclerView). However, I don't know how to fetch a single value from the database based on some criteria.

Following is my code:

User.kt

@Entity(tableName = TABLE_NAME)
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "email") val email: String,
    @ColumnInfo(name = "password") val password: String
)

We get TABLE_NAME from UserDAO.

UserViewModel.kt

class UserViewModel(private val repository: UserRepository) : ViewModel() {

    val allUsers: LiveData<List<User>> = repository.allUsers.asLiveData()

    /**
     * Launching a new coroutine to insert the data in a non-blocking way
     */
    fun insert(user: User) = viewModelScope.launch {
        repository.insert(user)
    }
}

class UserViewModelFactory(private val repository: UserRepository) :
    ViewModelProvider.Factory {
    
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return UserViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

UserRepository.kt

class UserRepository(private val userDAO: UserDAO) {
      
     val allUsers: Flow<List<User>> = userDAO.getAll()

     @Suppress("RedundantSuspendModifier")
     @WorkerThread
     suspend fun insert(user: User) {
          userDAO.insert(user)
     }
}

UserDAO.kt

@Dao
interface UserDAO {

    @Query("SELECT * FROM " + TABLE_NAME)
    fun getAll(): Flow<List<User>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(user: User)

    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)

    @Query("DELETE FROM " + TABLE_NAME)
    suspend fun deleteAll()


    companion object {
        const val TABLE_NAME: String = "user_table"
    }
}

UserDatabase.kt

@Database(entities = arrayOf(User::class), version = 1, exportSchema = false)
public abstract class UserDatabase : RoomDatabase() {

    abstract fun userDAO(): UserDAO

    private class UserDatabaseCallback(private val scope: CoroutineScope) :
        RoomDatabase.Callback() {
        
        override fun onCreate(db: SupportSQLiteDatabase) {
            super.onCreate(db)
            INSTANCE?.let { database ->
                scope.launch {
                    populateUsers(database.userDAO())
                }
            }
        }

        suspend fun populateUsers(userDAO: UserDAO) {
            userDAO.deleteAll()

            var user = User(name = "Admin", email = "[email protected]",
                password = "admin123")    
            userDAO.insert(user)
        }
    }

    companion object {
        /**
         * Singleton prevents multiple instances of database opening at the same time
         */
        @Volatile
        private var INSTANCE: UserDatabase? = null

        fun getDatabase(context: Context, scope: CoroutineScope): UserDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    UserDatabase::class.java,
                    "user_database"
                ).addCallback(UserDatabaseCallback(scope))
                    .build()
                INSTANCE = instance
                instance              // return instance
            }
        }
    }

MyApplication.kt

class MyApplication : Application() {

    val applicationScope = CoroutineScope(SupervisorJob())

    // Using by lazy so the database and the repository are only created
    // when they're needed rather than when the application starts
    val database by lazy { UserDatabase.getDatabase(this, applicationScope) }

    val userRepository by lazy { UserRepository(database.userDAO()) }
}

Now, I want to create a login activity and try to login a user based on email and password.

LoginActivity.kt

class LoginActivity : AppCompatActivity() {

    private val userViewModel: UserViewModel by viewModels {
        UserViewModelFactory((application as MyApplication).userRepository)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        btnLogin.setOnClickListener {
            val email=editEmail.text.toString()
            val password=editPassword.text.toString()
           
            /**
             * code to check whether user with this email and password exists or not
             */
        }
    }
}

I don't know how to fetch a user with matching email and password. Please help me with that.

Upvotes: 2

Views: 4545

Answers (2)

Rajan Kali
Rajan Kali

Reputation: 12953

You can add a method inside UserDao which will get the record of the user if available something like below

@Query("SELECT * FROM " + TABLE_NAME + " WHERE email = :email AND password = :password LIMIT 1")
fun isValidUser(email: String, password: String): Flow<User?>

Then in repository add a method to verify if acquired user is not null

 @WorkerThread
 suspend fun isValidUser(user: User): Flow<User?> {
      return userDAO.isValidUser(user.email, user.password)
 }

Then in ViewModel, add one more method like below

val userLiveData: LiveData<User?> = MutableLiveData()

/**
 * Launching a new coroutine to insert the data in a non-blocking way
 */
fun insert(user: User) = viewModelScope.launch {
    repository.insert(user)
}

fun validateUser(email: String, password: String)= viewModelScope.launch{
    repository.isValiduser(user(name = "",email = email, password = password 
        ).collect{
        userLiveData.postValue(it)
 }
    
}

Finally, in activity

btnLogin.setOnClickListener {
            val email=editEmail.text.toString()
            val password=editPassword.text.toString()
           
            userViewModel.validateUser(email, password)
        }

userViewModel.userLiveData.observe(this){
     if(it != null){
       //valid user
     }else{
        //invalid
     }
}

Upvotes: 3

Antonio
Antonio

Reputation: 239

You have to create function in DAO to do that, similar as you created.

@Query("SELECT * FROM " + TABLE_NAME)
fun getAll(): Flow<List<User>>

See this example and learn more about SQL

Upvotes: 1

Related Questions