android 如何插入和查询从API接收的包含一对多关系的房间数据库中的嵌套实体

h6my8fg2  于 2023-01-24  发布在  Android
关注(0)|答案(1)|浏览(83)

我有一个API,它返回一个包含实体的DTO,这些实体具有填充在列表中的一对多关系。
我很难弄清楚如何处理房间里的数据。
我需要将此DTOMap到相应的实体。然后,我需要将它们插入到数据库中。稍后,我需要查询它们并检索BoardEntity及其相应BoardChildEntities的列表

fun getBoards() = networkBoundResource(query = {
    // I need to query all boards from Room and add to that its corresponding children in a list
}, 
fetch = {
    api.getBoards()
},
saveFetchResult = { dtos ->
    // how can I save all the data in the DTOs in their corresponding tables
    // without writing a lot of nested loops
})

从API返回的DTO:

data class BoardDto(
    val id: Int,
    val name: String,
    val boardChildren: List<BoardChildDto>,
) {
    data class BoardChildDto(
        val id: Int,
        val boardId: Int, // foreign key
        val name: String,
        val boardElements: List<BoardElementDto>,
    ) {
        data class BoardElementDto(
            val id: Int,
            val boardChildId: Int, // foreign key
            val name: String,
            val type: String,
            val hyperlinks: List<BoardElementHyperlinkDto>,
        ) {
            data class BoardElementHyperlinkDto(
                val id: Int,
                val boardElementId: Int, // foreign key
                val name: String,
            )
        }
    }
}

房间实体:

@Entity
data class BoardEntity(
    @PrimaryKey(autoGenerate = false) val id: Int,
    val icon: String,
    val name: String,
)

@Entity(
    foreignKeys = [ForeignKey(
        entity = BoardEntity::class,
        parentColumns = ["id"],
        childColumns = ["boardId"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class BoardChildEntity(
    @PrimaryKey(autoGenerate = false) val id: Int,
    val boardId: Int,
    val name: String,
)

@Entity(
    foreignKeys = [ForeignKey(
        entity = BoardChildEntity::class,
        parentColumns = ["id"],
        childColumns = ["boardChildId"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class BoardElementEntity(
    @PrimaryKey(autoGenerate = false) val id: Int,
    val boardChildId: Int,
    val name: String,
)

@Entity(
    foreignKeys = [ForeignKey(
        entity = BoardElementEntity::class,
        parentColumns = ["id"],
        childColumns = ["boardElementId"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class BoardElementHyperlinkEntity(
    @PrimaryKey(autoGenerate = false) val id: Int,
    val boardElementId: Int,
    val name: String,
)

从DTO到文件室实体的Map器

fun BoardDto.toEntityModel(): BoardEntity {
    return BoardEntity(
        id = id,
        name = name,
    )
}

fun BoardChildDto.toEntityModel(): BoardChildEntity {
    return BoardChildEntity(
        id = id,
        boardId = boardId,
        name = name,
    )
}

fun BoardElementDto.toEntityModel(): BoardElementEntity {
    return BoardElementEntity(
        id = id,
        boardChildId = boardChildId,
        name = name,
    )
}

fun BoardElementHyperlinkDto.toEntityModel(): BoardElementHyperlinkEntity {
    return BoardElementHyperlinkEntity(
        id = id,
        boardElementId = boardElementId,
        name = name,
    )
}
tag5nh1u

tag5nh1u1#

//如何将DTO中的所有数据保存到对应的表中
//不需要编写大量嵌套循环
取决于你所谓的地段,有些是无法避免的。
下面是一个如何实现这一点的示例(来自一个@Dao注解接口):

@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardEntity: BoardEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardChildEntity: BoardChildEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardElementEntity: BoardElementEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(boardElementHyperlinkEntity: BoardElementHyperlinkEntity): Long

@Transaction
@Query("")
fun insertDTO(dto: BoardDto, icon: String): Int {
    val boardId = insert(BoardEntity( id = dto.id,name = dto.name, icon = icon))
    if (boardId < 0) return TheDatabase.BOARD_NOT_INSERTED
    for (bc in dto.boardChildren) {
        val boardChildId = insert(BoardChildEntity(id = bc.id, boardId = boardId.toInt(), name = bc.name))
        if (boardChildId < 0) return TheDatabase.BOARDCHILD_NOT_INSERTED
        for (be in bc.boardElements) {
            val boardElementId = insert(BoardElementEntity(id = be.id, boardChildId = boardChildId.toInt(), name = be.name))
            if (boardElementId < 0) return TheDatabase.BOARDELEMENT_NOT_INSERTED
            for (beh in be.hyperlinks) {
                val boardElementHyperlinkId = insert(BoardElementHyperlinkEntity(id = beh.id, boardElementId = boardElementId.toInt(), beh.name))
                if (boardElementHyperlinkId < 0) return TheDatabase.BOARDHYPERLINK_NOT_INESRTED
            }
        }
    }
    return boardId.toInt()
}

最初的4个标准便利插页
然后一个函数体包含3个嵌套循环(请注意,为了减少演示外键级联并覆盖硬代码值的麻烦(请参见演示和结果))。如果正确,可以使用实际值的(如果不是,那么你会遇到外键冲突)。类似地,如果一个插入在任何级别被忽略,那么就会立即返回,Int根据coming常量返回(-99到-96,包括-99和-96)(这种行为可以很容易地去除)。
稍后,我想查询它们并检索一个BoardEntity及其对应的BoardChildEntities列表
并且我假设具有子BoardElement以及HyperLink子项(向下贯穿所有层次结构)。
为此,您可以使用POJO的层次结构,每个POJO获取父项和子项。例如,以下POJO的父项具有@Embed注解,子项列表具有@Relation注解。

data class BoardElementEntityWithHyperlinkEntity(
    @Embedded
    val boardElementEntity: BoardElementEntity,
    @Relation( entity = BoardElementHyperlinkEntity::class, parentColumn = "id", entityColumn = "boardElementId")
    val boardElementHyperlinkEntityList: List<BoardElementHyperlinkEntity>
)

data class BoardChildEntityWithElementEntity(
    @Embedded
    val boardChildEntity: BoardChildEntity,
    @Relation( entity = BoardElementEntity::class, parentColumn = "id", entityColumn = "boardChildId")
    val boardElementEntityList: List<BoardElementEntityWithHyperlinkEntity>
)

data class BoardEntityWithBoardChildList(
    @Embedded
    val boardEntity: BoardEntity,
    @Relation(entity = BoardChildEntity::class, parentColumn = "id", entityColumn = "boardId")
    val BoardChildEntityList: List<BoardChildEntityWithElementEntity>
)
  • 注意在@Relation中,实体=是@Entity注解类而不是字段类。
  • POJO以相反的顺序列出,因为经验表明,从下往上创建它们更容易。
    演示

这是一个工作演示,运行时,take从BoardDTO添加数据,然后提取它(尽管替换了一些引用,如boardId)。
首先是整个数据库的代码,还有你的BoardDTo(你的Map器还没有被使用):-

data class BoardDto(
    val id: Int,
    val name: String,
    val boardChildren: List<BoardChildDto>,
) {
    data class BoardChildDto(
        val id: Int,
        val boardId: Int, // foreign key
        val name: String,
        val boardElements: List<BoardElementDto>,
    ) {
        data class BoardElementDto(
            val id: Int,
            val boardChildId: Int, // foreign key
            val name: String,
            val type: String,
            val hyperlinks: List<BoardElementHyperlinkDto>,
        ) {
            data class BoardElementHyperlinkDto(
                val id: Int,
                val boardElementId: Int, // foreign key
                val name: String,
            )
        }
    }
}

@Entity
data class BoardEntity(
    @PrimaryKey(autoGenerate = false) val id: Int,
    val icon: String,
    val name: String,
)

@Entity(
    foreignKeys = [ForeignKey(
        entity = BoardEntity::class,
        parentColumns = ["id"],
        childColumns = ["boardId"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class BoardChildEntity(
    @PrimaryKey(autoGenerate = false) val id: Int,
    val boardId: Int,
    val name: String,
)
data class BoardEntityWithBoardChildList(
    @Embedded
    val boardEntity: BoardEntity,
    @Relation(entity = BoardChildEntity::class, parentColumn = "id", entityColumn = "boardId")
    val BoardChildEntityList: List<BoardChildEntityWithElementEntity>
)

@Entity(
    foreignKeys = [ForeignKey(
        entity = BoardChildEntity::class,
        parentColumns = ["id"],
        childColumns = ["boardChildId"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class BoardElementEntity(
    @PrimaryKey(autoGenerate = false) val id: Int,
    val boardChildId: Int,
    val name: String,
)
data class BoardChildEntityWithElementEntity(
    @Embedded
    val boardChildEntity: BoardChildEntity,
    @Relation( entity = BoardElementEntity::class, parentColumn = "id", entityColumn = "boardChildId")
    val boardElementEntityList: List<BoardElementEntityWithHyperlinkEntity>
)

@Entity(
    foreignKeys = [ForeignKey(
        entity = BoardElementEntity::class,
        parentColumns = ["id"],
        childColumns = ["boardElementId"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class BoardElementHyperlinkEntity(
    @PrimaryKey(autoGenerate = false) val id: Int,
    val boardElementId: Int,
    val name: String,
)

data class BoardElementEntityWithHyperlinkEntity(
    @Embedded
    val boardElementEntity: BoardElementEntity,
    @Relation( entity = BoardElementHyperlinkEntity::class, parentColumn = "id", entityColumn = "boardElementId")
    val boardElementHyperlinkEntityList: List<BoardElementHyperlinkEntity>
)

@Dao
interface TheDAOs {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(boardEntity: BoardEntity): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(boardChildEntity: BoardChildEntity): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(boardElementEntity: BoardElementEntity): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(boardElementHyperlinkEntity: BoardElementHyperlinkEntity): Long

    @Transaction
    @Query("")
    fun insertDTO(dto: BoardDto, icon: String): Int {
        val boardId = insert(BoardEntity( id = dto.id,name = dto.name, icon = icon))
        if (boardId < 0) return TheDatabase.BOARD_NOT_INSERTED
        for (bc in dto.boardChildren) {
            val boardChildId = insert(BoardChildEntity(id = bc.id, boardId = boardId.toInt(), name = bc.name))
            if (boardChildId < 0) return TheDatabase.BOARDCHILD_NOT_INSERTED
            for (be in bc.boardElements) {
                val boardElementId = insert(BoardElementEntity(id = be.id, boardChildId = boardChildId.toInt(), name = be.name))
                if (boardElementId < 0) return TheDatabase.BOARDELEMENT_NOT_INSERTED
                for (beh in be.hyperlinks) {
                    val boardElementHyperlinkId = insert(BoardElementHyperlinkEntity(id = beh.id, boardElementId = boardElementId.toInt(), beh.name))
                    if (boardElementHyperlinkId < 0) return TheDatabase.BOARDHYPERLINK_NOT_INESRTED
                }
            }
        }
        return boardId.toInt()
    }

    @Transaction
    @Query("SELECT * FROM boardentity")
    fun getAllBoardsWithFamily(): List<BoardEntityWithBoardChildList>
}

@Database(
    entities = [
        BoardElementHyperlinkEntity::class,
        BoardElementEntity::class,
        BoardChildEntity::class,
        BoardEntity::class
               ],
    exportSchema = false,
    version = 1
)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getTheDAOs(): TheDAOs
    companion object {
        const val BOARD_NOT_INSERTED = -99
        const val BOARDCHILD_NOT_INSERTED = -98
        const val BOARDELEMENT_NOT_INSERTED = -97
        const val BOARDHYPERLINK_NOT_INESRTED = -96

        private var instance: TheDatabase?=null
        fun getInstance(context: Context): TheDatabase {
            if (instance==null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

要实际测试以下活动代码(MainActivity):-

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

        db = TheDatabase.getInstance(this)
        dao = db.getTheDAOs()

        val beh = BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(11100,9999,"BEH0001")
        val behlist1 = listOf(
            beh,
            BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(22200,9999,"BEH0002"),
            BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(33300,9999,"BEH0003")
        )
        val behlist2 = listOf(
            BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(44400,9999,"BEH0004"),
            BoardDto.BoardChildDto.BoardElementDto.BoardElementHyperlinkDto(55500,9999,"BEH0005")
        )
        val belist1 = listOf(
            BoardDto.BoardChildDto.BoardElementDto(id = 1100, boardChildId = 999, name = "BE0001", type = "A", hyperlinks = behlist1),
            BoardDto.BoardChildDto.BoardElementDto(id= 2200, boardChildId = 999, name = "BE0002", type = "B", hyperlinks = behlist2)
        )
        val bclist = listOf(
            BoardDto.BoardChildDto(id = 110, boardId = 99, name = "BC0001", boardElements = belist1 ),
            BoardDto.BoardChildDto(id = 220, boardId = 99, name = "BC0002", boardElements = belist1 ),
        )

        dao.insertDTO(BoardDto(id = 11, boardChildren = bclist, name = "B00001"), icon = "unsure")

        for (b in dao.getAllBoardsWithFamily()) {
            Log.d(TAG,"Board Name is ${b.boardEntity.name} ICON is ${b.boardEntity.icon} ID is ${b.boardEntity.id}. Board has ${b.BoardChildEntityList.size} BoardChildren. They are:-")
            for (bc in b.BoardChildEntityList) {
                Log.d(TAG,"\tBC Name is ${bc.boardChildEntity.name} ID is ${bc.boardChildEntity.id} Mapped To Board ${bc.boardChildEntity.boardId}. ${bc.boardElementEntityList.size} elements:-")
                for(be in bc.boardElementEntityList) {
                    Log.d(TAG,"\t\tBE Name is ${be.boardElementEntity.name} ${be.boardElementHyperlinkEntityList.size} elemets:-")
                    for (beh in be.boardElementHyperlinkEntityList) {
                        Log.wtf(TAG,"\t\t\tBEH name is ${beh.name} ID is ${beh.id} maps to ${beh.boardElementId}")
                    }
                }
            }
        }
    }
    companion object {
        const val TAG = "DBINFO"
    }
}
  • 请注意,此代码仅用于运行一次

运行时,输出到日志的结果为:-

D/DBINFO: Board Name is B00001 ICON is unsure ID is 11. Board has 2 BoardChildren. They are:-
D/DBINFO:   BC Name is BC0001 ID is 110 Mapped To Board 11. 2 elements:-
D/DBINFO:       BE Name is BE0001 3 elemets:-
D/DBINFO:           BEH name is BEH0001 ID is 11100 maps to 1100
D/DBINFO:           BEH name is BEH0002 ID is 22200 maps to 1100
D/DBINFO:           BEH name is BEH0003 ID is 33300 maps to 1100
D/DBINFO:       BE Name is BE0002 2 elemets:-
D/DBINFO:           BEH name is BEH0004 ID is 44400 maps to 2200
D/DBINFO:           BEH name is BEH0005 ID is 55500 maps to 2200
D/DBINFO:   BC Name is BC0002 ID is 220 Mapped To Board 11. 0 elements:-
  • 注意BC 0002将没有孩子,因为该列表与BC 0001的列表相同,因此重复的将被跳过。

通过应用程序检查数据库:-

相关问题