Reputation: 801
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
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