Issue
My goal is to save data that the user inputs in a field ( my case outlinedtextfield ) and store that data both on screen for the user to see and in a local database using room. I have created the basics of room ( Database, Dao, Data class, Repository, RepositoryImpl and a viewmodel ), but I cant figure out how to save the user input into it/ take user input and save it to the database I created. I want to save both string input and Int input. How can I achieve this while following best practices with dependency injection ?
My current information:
Main Activity: https://gyazo.com/163dfa890d4ac10c2bd28c7876f25b4a
Data class:
@Entity(tableName = "student_table")
data class Student(
@PrimaryKey(autoGenerate = true) val id: Int?,
@ColumnInfo(name = "first_name") val firstName: String?,
@ColumnInfo(name = "last_name") val lastName: String?,
@ColumnInfo(name = "phone_number") val phoneNumber: Int?
)
My Dao:
@Dao
interface StudentDao {
@Query("SELECT * FROM student_table")
fun getAll(): Flow<List<Student>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(student: Student)
@Update
fun update(student: Student)
@Delete
fun delete(student: Student)
@Delete
fun deleteAll(student: Student)
}
My Database:
@Database(entities = [Student::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun studentDao() : StudentDao
companion object {
@Volatile
private var INSTANCE : AppDatabase? = null
fun getDatabase(context: Context) : AppDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
My repository:
interface StudentRepository {
suspend fun getAllStudentsFromRoom(): Flow<List<Student>>
suspend fun addStudent(student: Student)
suspend fun updateStudent(student: Student)
suspend fun deleteStudent(student: Student)
}
My Implementation Repository
class StudentRepositoryImpl(
private val studentDao: StudentDao
) : StudentRepository {
override suspend fun getAllStudentsFromRoom(): Flow<List<Student>> = studentDao.getAll()
override suspend fun addStudent(student: Student) = studentDao.insert(student)
override suspend fun updateStudent(student: Student) = studentDao.update(student)
override suspend fun deleteStudent(student: Student) = studentDao.delete(student)
}
My ViewModel:
@HiltViewModel
class StudentViewModel @Inject constructor(
private val repo: StudentRepository
) : ViewModel() {
fun addStudent(student: Student) = viewModelScope.launch(Dispatchers.IO) {
repo.addStudent(student)
}
fun updateStudent(student: Student) = viewModelScope.launch(Dispatchers.IO) {
repo.updateStudent(student)
}
fun deleteStudent(student: Student) = viewModelScope.launch(Dispatchers.IO) {
repo.deleteStudent(student)
}
}
Solution
Update 2022.07.29
I managed to solve the problem. There was a few things missing that I needed to add in order for it to work.
I needed to attribute @AndroidEntryPoint on the mainActivity.
I needed to create an application class for my project: https://gyazo.com/326ac277d951814cf77a255afa438794
After creating the application class, I had to "call it" in the Android manifest by adding it in the following format in the application: "android:name=".application.MyDatabaseProjekt2022" Like this:
I also needed to create an appModule class, I did this by doing the following: https://gyazo.com/0fed4a454d56e696c08cd98f02c7c98f
I also took some information suggested by FishHawk and created a simple UI to display the information. This is the result:
@Composable
fun Test() {
val viewModel = viewModel<StudentViewModel>()
Column {
var id: Int by rememberSaveable { mutableStateOf(0) }
var firstName by rememberSaveable { mutableStateOf("") }
var lastName by rememberSaveable { mutableStateOf("") }
var phoneNumber by rememberSaveable { mutableStateOf("") }
Spacer(modifier = Modifier.height(20.dp))
OutlinedTextField(value = firstName, onValueChange = { firstName = it },
label = { Text(text = "First Name")},
singleLine = true,
)
Spacer(modifier = Modifier.height(20.dp))
OutlinedTextField(value = lastName, onValueChange = { lastName = it },
label = { Text(text = "Last Name")},
singleLine = true
)
Spacer(modifier = Modifier.height(20.dp))
OutlinedTextField(
value = phoneNumber,
onValueChange = { phoneNumber = it },
label = { Text(text = "Phone Number")},
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Phone),
singleLine = true,
)
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
val realPhoneNumber = phoneNumber
val student = Student(id,firstName, lastName, realPhoneNumber)
viewModel.addStudent(student)
}) { Text(text = "Save Student") }
}
}
In order to verify whether or not the information gets saved in the database, do the following. Run the app and go to: View -> Tool windows -> App inspection
Type in the information (firstname, lastname, phonenumber), press the save student button and database should show up like this: https://gyazo.com/b85766f0ffa4470036cf5d7c4dd9d9f7
Double tap student_table and another window next to that should pop up with the saved information that you put in.
Hopefully this is everything you need in order to make a functional and working database. This took me around 2 months to figure out ( I do programming as a hobby and Im still in the "early stage" ). Hopefully someone here will find it useful. Happy coding!
Answered By - Josef M
Answer Checked By - Gilberto Lyons (JavaFixing Admin)