Room 上的嵌套嵌入式列表

Nested embedded lists on Room

我正在尝试使用空间 Something 类型的对象,其中包含一个 Stuff 列表,并且该列表的每个项目都包含另一个 Things 列表。

data class rootWrapper(
        @Embeded
        var somethingEntity: SomethingEntity
         @Relation(
            parentColumn = "id",
            entityColumn = "idSomething",
            associatedBy = Junction(SomethingWithStuffEntity::class)
     )
     var listOfStuff: MutableList<StuffWithThingsInsideWrapper>
)

data class StuffWithThingsInsideWrapper(
        @Embeded
        var stuff: stuffEntity
         @Relation(
            parentColumn = "id",
            entityColumn = "idStuff",
            associatedBy = Junction(StuffWithThingsEntity::class)
     )
     var listOfThings: MutableList<ThingsEntity>
)

问题是因为 StuffWithThingsInsided 没有 ID。我得到一个 Cannot find the parent entity column id in com.test.room.entity.StuffWithThingsWapper。我不知道我想要达到的目标是否有可能,我在那里找不到任何例子。有什么建议吗?

I don't know if what I'm trying to achieve is even possible, I couldn't find any example out there. Any suggestion?

有可能。或许考虑以下。

当您 associateBy 时,您是在说您正在使用关联 table 访问父项和子项。

这样的 table 将至少有两列,在您的情况下,一列引用 stuffEntity,另一列引用相关的 ThingEntity。

因此,@RelationassociateBy 涉及 4 列:-

  • 父级,即 @Embedded
  • 子(ren)即@Relation
  • 关联 table 中引用 @Embedded
  • 的列
  • 关联 table 中引用 @Relation
  • 的列

前两个根据@Relation 的父参数和实体参数,后两个根据'Junction'。

此外,当包含 hierarchy/levels 时,您遇到的情况更复杂,即变量匹配的列需要从属的 parent 实体,即不在下属。您需要将实体 class 指定为各自的实体,而不是被提取的 POJO 的 class。

所以你可以有类似的东西:-

data class StuffWithThingsInsideWrapper(
    @Embedded
    var stuff: StuffEntity,
    @Relation(
        entity = ThingsEntity::class,
        parentColumn = "id",
        entityColumn = "id",
        associateBy = (
                Junction(
                    value = StuffWithThingsEntity::class,
                    parentColumn = "idStuff",
                    entityColumn = "idThings"
                )
                )
    )
    var listOfThings: List<ThingsEntity>
)

和:-

data class RootWrapper(
    @Embedded
    var somethingEntity: SomethingEntity,
    @Relation(
        entity = StuffEntity::class, //<<<<< The entity behind a Something With Stuff
        parentColumn = "id",
        entityColumn = "id",
        associateBy = (
                Junction(
                    SomethingWithStuffEntity::class,
                    parentColumn = "idSomething",
                    entityColumn = "idStuff"
                )
                )
    )
    var listOfStuff: List<StuffWithThingsInsideWrapper>
)
  • 注意 由于未包含基础实体,因此已猜测列名。

工作Example/Demo

除了上述之外,还使用了以下 classes :-

事物

@Entity
data class ThingsEntity(
    @PrimaryKey
    var id: Long? = null,
    var name: String
)

东西

@Entity
data class StuffEntity(
    @PrimaryKey
    var id: Long? = null,
    var name: String
)

某事

@Entity
data class SomethingEntity(
    @PrimaryKey
    var id: Long? = null,
    var name: String
)

StuffWithThings

@Entity(
    primaryKeys = ["idStuff","idThings"],
    foreignKeys = [
        ForeignKey(
            entity =  StuffEntity::class,
            parentColumns = ["id"],
            childColumns = ["idStuff"]
        ), ForeignKey(
            entity = ThingsEntity::class,
            parentColumns = ["id"],
            childColumns = ["idThings"]
        )
    ]
)
data class StuffWithThingsEntity(
    var idStuff: Long,
    @ColumnInfo(index = true)
    var idThings: Long
)
  • 外键是可选的,它们强制引用完整性

SomethingWithStuffEntity

@Entity(
    primaryKeys = ["idSomething","idStuff"],
    foreignKeys = [
        ForeignKey(
            entity = SomethingEntity::class,
            parentColumns = ["id"],
            childColumns = ["idSomething"]
        ), ForeignKey(
            entity =  StuffEntity::class,
            parentColumns = ["id"],
            childColumns = ["idStuff"])
    ]
)
data class SomethingWithStuffEntity(
    var idSomething: Long,
    @ColumnInfo(index = true)
    var idStuff: Long
)

AllDao

@Dao
interface AllDao {
    @Insert
    fun insert(thingsEntity: ThingsEntity): Long
    @Insert
    fun insert(stuffEntity: StuffEntity): Long
    @Insert
    fun insert(somethingEntity: SomethingEntity): Long
    @Insert
    fun insert(stuffWithThingsEntity: StuffWithThingsEntity): Long
    @Insert
    fun insert(somethingWithStuffEntity: SomethingWithStuffEntity)
    @Query("SELECT * FROM somethingentity")
    fun getRootWrapperList(): List<RootWrapper>

}

数据库

@Database(entities = [ThingsEntity::class,StuffEntity::class,SomethingEntity::class,StuffWithThingsEntity::class,SomethingWithStuffEntity::class], version = 1)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDao(): AllDao

    companion object {
        private var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(
                    context,
                    TheDatabase::class.java,
                    "thedatabase.db"
                )
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}
  • for brevity/convenience .allowMainThreadQueries 已被使用。

最后把它们放在一起 activity :-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: AllDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this)
        dao = db.getAllDao()

        val thing1Id = dao.insert(ThingsEntity(name = "Thing1"))
        val thing2Id = dao.insert(ThingsEntity(name = "Thing2"))
        val thing3Id = dao.insert(ThingsEntity(name = "Thing3"))
        val stuff1Id = dao.insert(StuffEntity(name = "Stuff1"))
        val stuff2Id = dao.insert(StuffEntity(name = "Stuff2"))
        val something1Id = dao.insert(SomethingEntity(name = "Something1"))
        val something2Id = dao.insert(SomethingEntity(name = "Something2"))
        val something3Id = dao.insert(SomethingEntity(name = "Something3"))

        dao.insert(StuffWithThingsEntity(stuff1Id,thing1Id))
        dao.insert(StuffWithThingsEntity(stuff1Id,thing2Id))
        dao.insert(StuffWithThingsEntity(stuff1Id,thing3Id))
        dao.insert(StuffWithThingsEntity(stuff2Id,thing3Id))
        dao.insert(StuffWithThingsEntity(stuff2Id,thing2Id))

        dao.insert(SomethingWithStuffEntity(something1Id,stuff1Id))
        dao.insert(SomethingWithStuffEntity(something1Id,stuff2Id))
        dao.insert(SomethingWithStuffEntity(something2Id,stuff2Id))

        for(rw: RootWrapper in dao.getRootWrapperList()) {
            Log.d("RWINFO","Something is ${rw.somethingEntity.name}")
            for (swt: StuffWithThingsInsideWrapper in rw.listOfStuff) {
                Log.d("RWINFO","\tStuff is ${swt.stuff.name}")
                for (t: ThingsEntity in swt.listOfThings) {
                    Log.d("RWINFO","\t\tThings is ${t.name}")
                }
            }
        }
    }
}

运行上面(第一次)那么输出到日志的结果是:-

2021-11-25 07:04:58.828 D/RWINFO: Something is Something1
2021-11-25 07:04:58.828 D/RWINFO:   Stuff is Stuff1
2021-11-25 07:04:58.828 D/RWINFO:       Things is Thing1
2021-11-25 07:04:58.828 D/RWINFO:       Things is Thing2
2021-11-25 07:04:58.828 D/RWINFO:       Things is Thing3
2021-11-25 07:04:58.828 D/RWINFO:   Stuff is Stuff2
2021-11-25 07:04:58.828 D/RWINFO:       Things is Thing2
2021-11-25 07:04:58.828 D/RWINFO:       Things is Thing3

2021-11-25 07:04:58.828 D/RWINFO: Something is Something2
2021-11-25 07:04:58.828 D/RWINFO:   Stuff is Stuff2
2021-11-25 07:04:58.829 D/RWINFO:       Things is Thing2
2021-11-25 07:04:58.829 D/RWINFO:       Things is Thing3

2021-11-25 07:04:58.829 D/RWINFO: Something is Something3