I am experimenting with Room , Live Data and Recycler View in Android Kotlin .
My question is, I am trying to make an expense tracking APP, and I have 2 Table:
I joined the table as indicated in Room documentation for 1:N relationship .
Example of my table:
**Expense**
*ID = 1
expenseName = MyExpense1
expenseAmount = 100
expenseTypeID = 1*
**ExpenseType**
*ID= 1
ExpenseType= Home Expenses*
**Result expected from JOIN:**
*expenseName = MyExpense1
expenseAmount = 100
expenseType = Home Expenses*
But in this way, when I get the data for recycler view, I get a list that contain:
How can I have data as if **I JOINED ** the table? Since my **ExpenseTypeWithExpense **class contains a class and a List of class
Usually I use a **RecycleView **on just one table and it is easy since I have a list of my Entity Class and I can access the single instance with list[position] in my **onBindViewHolder **class
@Entity(
foreignKeys =[
ForeignKey(
entity = ExpenseType::class,
parentColumns = ["id"],
childColumns = ["expenseTypeID"]
)]
)
data class Expense (
@PrimaryKey(autoGenerate = true)
val id:Int,
val expenseName: String,
val expenseAmount: Double,
val expenseTypeID:Int
)
@Entity(tableName = "expense_type")
data class ExpenseType (
@PrimaryKey(autoGenerate = true)
val id:Int,
val expenseType: String
)
data class ExpenseTypeWithExpense (
@Embedded val expenseType: ExpenseType,
@Relation(
parentColumn ="id",
entityColumn ="id"
)
val expense: List<Expense>
)
@Dao
interface ExpenseDao {
@Insert
suspend fun insertExpense(expense: Expense)
@Insert
suspend fun insertExpenseType(expenseType:ExpenseType)
@Transaction
@Query("SELECT * FROM expense_type")
fun getExpenseWithType():LiveData<List<ExpenseTypeWithExpense>>
}
class ExpenseRepository(private val expenseDao: ExpenseDao) {
val readAllData: LiveData<List<ExpenseTypeWithExpense>> = expenseDao.getExpenseWithType()
suspend fun insertExpense(expense: Expense){
expenseDao.insertExpense(expense)
}
suspend fun insertExpenseType(expenseType: ExpenseType){
expenseDao.insertExpenseType(expenseType)
}
}
class ExpenseViewModel(application: Application):AndroidViewModel(application) {
val readAllData: LiveData<List<ExpenseTypeWithExpense>>
private val repository: ExpenseRepository
init {
val expenseDao: ExpenseDao = ExpenseDatabase.getDatabase(application).expenseDao()
repository = ExpenseRepository(expenseDao)
readAllData = expenseDao.getExpenseWithType()
}
fun insertExpense(expense: Expense){
viewModelScope.launch(Dispatchers.IO){
repository.insertExpense(expense)
}
}
fun insertExpenseType(expenseType: ExpenseType){
viewModelScope.launch(Dispatchers.IO){
repository.insertExpenseType(expenseType)
}
}
}
class ListAdapter(): RecyclerView.Adapter<ListAdapter.MyViewHolder>() {
private val expenseList = emptyList<ExpenseTypeWithExpense>()
class MyViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.custom_row, parent, false))
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = expenseList[position]
holder.itemView.findViewById<TextView>(R.id.expenseName).text =
# ** //currentItem is an instance of ExpenseTypeWithExpense and contains ExpenseType class and an Expense List**
}
override fun getItemCount(): Int {
return expenseList.size
}
}
I do not Know how to handle this...
When you use @Relation
there is no actual JOIN, rather for each parent (the @Embedded) the children are obtained as a list, as you have found.
If you want the true cartesian product then you would use a POJO without the @Relation but rather @Embeded
and the query would have the JOIN.
eg
data class ExpenseTypeWithExpense (
@Embedded val expenseType: ExpenseType,
@Embedded
val expense: Expense
)
The query could then be:-
@Query("SELECT * FROM expense_type JOIN expense ON expense_type.id = expense.expenseTypeID;")
However , as the id field is common to both and that rather than objects (ExpenseType and Expense being included in the output) You probably want the POJO to be
:-
data class ExpenseTypeWithExpense (
val idOfExpenseType:Int,
val expenseType: String,
val id:Int,
val expenseName: String,
val expenseAmount: Double,
val expenseTypeID:Int
)
And the Query to (to rename the output columns so they can be assigned to the fields) as
@Query("SELECT expense_type.id AS idOfExpenseType, expense_type.expenseType, expense.* FROM expense_type JOIN expense ON expense_type.id = expense.expenseTypeID;;")
If you had expense_type as:-
id expenseType
1 T1
2 T2
3 T3
and expense as:-
id expenseName expenseAmount expenseTypeID
1 EXP1 100.11 1
2 EXP2 200.22 1
3 EXP3 300.33 1
4 EXP4 400.44 2
5 EXP5 500.55 2
Then the cartesian product output would be:-
which would equate to 5 ExpenseTypeWithExpense objects being returned in the LiveData<List>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.