简体   繁体   English

Join 2 Table with Android Kotlin Room 并在 RecycleView 的单行中显示纯数据

[英]Join 2 Table with Android Kotlin Room and display plain data in a single Row in RecycleView

I am experimenting with Room , Live Data and Recycler View in Android Kotlin .我正在Android Kotlin中试验RoomLive DataRecycler View

My question is, I am trying to make an expense tracking APP, and I have 2 Table:我的问题是,我正在尝试制作一个费用跟踪应用程序,并且我有 2 个表:

  • one for the expense一个用于费用
  • one for expense type一种用于费用类型

I joined the table as indicated in Room documentation for 1:N relationship .按照 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:但是通过这种方式,当我获取回收者视图的数据时,我得到一个包含以下内容的列表:

  • Expense Type Class费用类型 Class
  • Expense list of Expense Class Expense的费用清单 Class

How can I have data as if **I JOINED ** the table?我怎样才能像 **I JOINED ** 表一样拥有数据? Since my **ExpenseTypeWithExpense **class contains a class and a List of class由于我的 **ExpenseTypeWithExpense **类包含 class 和 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通常我只在一张表上使用 **RecycleView **,这很容易,因为我有一个实体列表 Class,我可以在我的 **onBindViewHolder **类中使用列表[位置]访问单个实例

EXPENSE Class费用 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
    )

EXPENSE TYPE Class费用类型 Class

@Entity(tableName = "expense_type")
data class ExpenseType (
    @PrimaryKey(autoGenerate = true)
    val id:Int,
    val expenseType: String
    )

EXPENSE TYPE Join with EXPENSE Class (as per documentation of joining 1:n table) EXPENSE TYPE 加入 EXPENSE Class(根据加入 1:n 表的文档)


data class ExpenseTypeWithExpense (
    @Embedded val expenseType: ExpenseType,
    @Relation(
        parentColumn ="id",
        entityColumn ="id"
    )
    val expense: List<Expense>
    )

My DAO Interface我的 DAO 接口

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

Expense Repository费用库

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

ExpenseViewModel费用视图模型

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

}

My Adapter我的适配器

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
    }

}

** //currentItem is an instance of ExpenseTypeWithExpense and contains ExpenseType class and an Expense List** ** ** //currentItem 是 ExpenseTypeWithExpense 的实例,包含 ExpenseType class 和一个 Expense List** **

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.当您使用@Relation时,没有实际的 JOIN,而是对于每个父母(@Embedded),孩子都是作为列表获得的,正如您所发现的那样。

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.如果您想要真正的笛卡尔积,那么您将使用不带 @Relation 的 POJO,而是使用@Embeded ,并且查询将具有 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但是,由于id字段对两者都是通用的,而不是对象(输出中包含 ExpenseType 和 Expense),您可能希望 POJO 是

  • When Room maps output columns to the fields, if there are any liked named columns the last is mapped.当 Room 将 output 列映射到字段时,如果有任何喜欢的命名列,则最后一个被映射。

:- :-

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查询到(重命名 output 列,以便将它们分配给字段)为

@Query("SELECT expense_type.id AS idOfExpenseType, expense_type.expenseType, expense.* FROM expense_type JOIN expense ON expense_type.id = expense.expenseTypeID;;")
  • as you can see the id column of the expense_type has been given a different name to map to the POJO.如您所见,expense_type 的 id 列已被赋予了与 POJO 不同的名称 map。 As * is not being used the expenseType has to be selected.由于未使用 *,因此必须选择费用类型。 However rather than specify all the columns of the expense table individually,expense.* is used to output all of the columns of the expense table.但是,不是单独指定费用表的所有列,expense.* 用于 output 费用表的所有列。

If you had expense_type as:-如果您的 expense_type 为:-

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:-那么笛卡尔积 output 将是:-

在此处输入图像描述

which would equate to 5 ExpenseTypeWithExpense objects being returned in the LiveData<List>这相当于在 LiveData<List> 中返回 5 个 ExpenseTypeWithExpense 对象

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM