如何通过 View Model、Repository 和 DAO 从 Room 数据库中读取值?

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

我正在使用 Room 数据库开发 Android 应用程序。我已经学会了将值写入数据库。就 reading/fetching 而言,我只能获得与从数据库中获取整个值列表相关的帮助(并将它们填充到 RecyclerView 中)。但是,我不知道如何根据某些条件从数据库中获取单个值。

以下是我的代码:

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
)

我们从 UserDAO 得到 TABLE_NAME

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 = "admin@example.com",
                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()) }
}

现在,我想创建一个登录 activity 并尝试根据电子邮件和密码登录用户。

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
             */
        }
    }
}

我不知道如何获取具有匹配电子邮件和密码的用户。 请帮助我。

您必须在 DAO 中创建函数才能执行此操作,类似于您创建的函数。

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

example and learn more about SQL

您可以在 UserDao 中添加一个方法,如果可用,它将获取用户的记录,如下所示

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

然后在存储库中添加一个方法来验证获取的用户是否不为空

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

然后在 ViewModel 中,再添加一个如下所示的方法

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

最后,在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
     }
}