android - 与 Room 和 LiveData 的多对多关系
问题描述
我有一个休息 api,它返回一个地点列表,其中有一个类别列表:
{
"id": "35fds-45sdgk-fsd87",
"name" : "My awesome place",
"categories" : [
{
"id": "cat1",
"name" : "Category 1"
},
{
"id": "cat2",
"name" : "Category 2"
},
{
"id": "cat3",
"name" : "Category 3"
}
]
}
因此,使用改造我从远程服务器获得这些模型类:
data class Category(var id: String, var name: String)
data class Place(
var id: String,
var name: String,
var categories: List<Category>
)
问题是——我希望 viewModel 总是从返回 Flowables 的本地 Room 数据库中检索,并且只触发刷新操作来更新数据库,从而更新视图。
DAO方法示例:
@Query("select * from Places where placeId = :id")
fun getPlace(id: String): Flowable<Place>
所以我尝试像这样对这两个类进行建模:
@Entity
data class Category(var id: String, var name: String)
@Entity
data class Place(
@PrimaryKey
var id: String,
var name: String,
var categories: List<Category>
)
但是,Room 当然不能自己处理关系。我看过这篇文章,它只是从本地数据库中检索以前的城市列表,但这种情况与那个不匹配。
我能想到的唯一选择是将数据库中的类别保存为 JSON 字符串,但这会失去数据库的关系质量......
这似乎是一个非常常见的用例,但我没有找到太多关于它的信息。
解决方案
Room 中可能存在多对多关系。
首先@Ignore
为您的课程添加注释Place
。它将告诉 Room 忽略此属性,因为它无法在没有转换器的情况下保存对象列表。
data class Category(
@PrimaryKey var id: String,
var name: String
)
data class Place(
@PrimaryKey var id: String,
var name: String,
@Ignore var categories: List<Category>
)
然后创建一个类来表示这两个类之间的连接。
@Entity(primaryKeys = ["place_id", "category_id"],
indices = [
Index(value = ["place_id"]),
Index(value = ["category_id"])
],
foreignKeys = [
ForeignKey(entity = Place::class,
parentColumns = ["id"],
childColumns = ["place_id"]),
ForeignKey(entity = Category::class,
parentColumns = ["id"],
childColumns = ["category_id"])
])
data class CategoryPlaceJoin(
@ColumnInfo(name = "place_id") val placeId: String,
@ColumnInfo(name = "category_id") val categoryId: String
)
如您所见,我使用了外键。
现在你可以指定特殊的 DAO 来获取一个地方的类别列表。
@Dao
interface PlaceCategoryJoinDao {
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
@Query("""
SELECT * FROM category INNER JOIN placeCategoryJoin ON
category.id = placeCategoryJoin.category_id WHERE
placeCategoryJoin.place_id = :placeId
""")
fun getCategoriesWithPlaceId(placeId: String): List<Category>
@Insert
fun insert(join: PlaceCategoryJoin)
}
最后一件重要的事情是每次插入 new 时插入 join 对象Place
。
val id = placeDao().insert(place)
for (place in place.categories) {
val join = CategoryPlaceJoin(id, category.id)
placeCategoryJoinDao().insert(join)
}
现在,当您从placeDao()
他们那里获得空类别列表时。为了添加类别,您可以使用这部分代码:
fun getPlaces(): Flowable<List<Place>> {
return placeDao().getAll()
.map { it.map { place -> addCategoriesToPlace(place) } }
}
private fun addCategoriesToPlace(place: Place): Place {
place.categories = placeCategoryJoinDao().getCategoriesWithPlaceId(place.id)
return place
}
要查看更多详细信息,请参阅这篇文章。
推荐阅读
- python-3.x - 无法使用python发送电子邮件
- d3.js - 有没有更好的方法来标记每个组?
- javascript - 如何使用在 ie6 环境中运行但不一定在浏览器中运行的纯 javascript 将带有压缩文件的多/表单数据上传到 rest api
- javascript - 如何使用 css 动画修复超链接错误
- c++ - vector::reserve 有什么问题?
- sql - SQL 代码错误地删除重复值
- powershell - 如何使用powershell从http下载文件和文件夹及其所有内容
- firefox - 如何让 Firefox 使用代理服务器进行 localhost 连接
- apache-camel - 测试 apache fuse fabric (fuse 6.3) 对所需步骤感到困惑
- graphql - Apollo 查询不返回使用 readFragment 可用的缓存数据