Android Room belongsTo(一对一)关系



假设我有一个这样的文章表:

Article {
id;
title;
content;
main_reference_id;
secondary_reference_id;
}

有一个类似这样的参考表:

Reference {
id;
other_reference_related_columns...;
}

现在我想获取文章,同时也获取main reference,secondary reference与另一个POJO像这样:

data class ArticleFull(
@Embedded
val article: article,
@Relation(parentColumn = "main_reference_id", entityColumn = "id")
val main_reference: Reference,
@Relation(parentColumn = "secondary_reference_id", entityColumn = "id")
val other_reference: Reference
)

但我不确定我写的是@Relation注释的正确用法。

注意::我是Laravel/Eloquent出身,所以对belongsTohasOnehasManybelongsToMany等关系类型比较熟悉。

谢谢。

但我不确定我写的是@Relation注释的正确用法。

可以。

下面是一个工作示例。这显示了ArticleFullPOJO的使用:-

首先是实体(表):-

参考: -

@Entity
data class Reference(
@PrimaryKey
val id: Long? = null,
val other_data: String
)

文章: -

@Entity(
foreignKeys = [
ForeignKey(
entity = Reference::class,
parentColumns = ["id"],
childColumns = ["main_reference_id"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = Reference::class,
parentColumns = ["id"],
childColumns = ["secondary_reference_id"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class Article(
@PrimaryKey
val id: Long? = null,
val title: String,
val content: String,
@ColumnInfo(index = true)
val main_reference_id: Long,
@ColumnInfo(index = true)
val secondary_reference_id: Long
)
添加了
  • 外键约束,它们有助于强制引用完整性,它们是可选的。onDelete和onUpdate外键是可选的。

一个@Dao类(抽象类而不是接口,抽象类更通用)ArticleAndReferenceDao: -

@Dao
abstract class ArticleAndReferenceDao {
@Insert
abstract fun insert(reference: Reference): Long
@Insert
abstract fun insert(article: Article): Long
@Transaction
@Query("SELECT * FROM article")
abstract fun getAllArticleFull(): List<ArticleFull>
@Transaction@Query("SELECT * FROM article WHERE id=:articleId")
abstract fun getArticleFullByArticleId(articleId: Long): List<ArticleFull>
}
@Database类ArticleDatabase: -
@Database(entities = [Reference::class,Article::class],version = 1)
abstract class ArticleDatabase: RoomDatabase() {
abstract fun getArticleAndReferenceDao(): ArticleAndReferenceDao
companion object {
@Volatile
private var instance: ArticleDatabase? = null
fun getArticleDatabaseInstance(context: Context): ArticleDatabase {
if(instance == null) {
instance = Room.databaseBuilder(
context,
ArticleDatabase::class.java,
"article.db"
)
.allowMainThreadQueries()
.build()
}
return instance as ArticleDatabase
}
}
}

最后一些活动代码,注意,为了方便和简洁,.allowMainThreadQueries已经被使用,允许代码在主线程上运行:-

class MainActivity : AppCompatActivity() {
lateinit var articleDatabase: ArticleDatabase
lateinit var articleAndReferenceDao: ArticleAndReferenceDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
articleDatabase = ArticleDatabase.getArticleDatabaseInstance(this)
articleAndReferenceDao = articleDatabase.getArticleAndReferenceDao()
val ref1 = articleAndReferenceDao.insert(Reference(other_data = "Reference1"))
val ref2 = articleAndReferenceDao.insert(Reference(other_data = "Reference2"))
val ref3 = articleAndReferenceDao.insert(Reference(other_data = "Reference3"))
val ref4 = articleAndReferenceDao.insert(Reference(other_data = "Reference4"))
articleAndReferenceDao.insert(Article(title = "Article1",main_reference_id = ref1,secondary_reference_id = ref2, content = "Content for Article1"))
articleAndReferenceDao.insert(Article(title = "Article2", main_reference_id = ref3, secondary_reference_id = ref4,content = "Content for Article2"))
// AND/OR
articleAndReferenceDao.insert(
Article(
title = "Article3",
content = "Content for Article 3",
main_reference_id = articleAndReferenceDao.insert(Reference(other_data = "Reference5")),
secondary_reference_id = articleAndReferenceDao.insert(Reference(other_data = "reference6"))
)
)
for(d: ArticleFull in articleAndReferenceDao.getAllArticleFull()) {
Log.d("ARTICLEINFO"," Article is ${d.article.content} ID is ${d.article.id} " +
"ntMain Reference is ${d.main_reference.other_data} ID is ${d.main_reference.id}" +
"ntSecondary  Reference is ${d.other_reference.other_data} ID is ${d.other_reference.id}")
}  
}
}

运行上面的结果在日志中包含:-

D/ARTICLEINFO:  Article is Content for Article1 ID is 1 
Main Reference is Reference1 ID is 1
Secondary  Reference is Reference2 ID is 2
D/ARTICLEINFO:  Article is Content for Article2 ID is 2 
Main Reference is Reference3 ID is 3
Secondary  Reference is Reference4 ID is 4
D/ARTICLEINFO:  Article is Content for Article 3 ID is 3 
Main Reference is Reference5 ID is 5
Secondary  Reference is reference6 ID is 6

另外

你也可以对这三个部分使用@Embedded。

优点是:-

  • 更灵活的过滤,即您可以对子节点进行过滤(虽然可以使用@Relationship,但您需要定义JOIN)
  • 与对@Relationship的多个底层查询不同,一个查询可以检索所有数据

缺点是:-

  • 更复杂的查询
  • 要求使用@ColumnInfo的prefix =注释,如果列名不是唯一的,以消除它们的歧义,因此更复杂的查询,以命名输出列。

所以你可以写:-

data class ArticleFullAlternative(
@Embedded
val article: Article,
@Embedded(prefix = "main_")
val main_reference: Reference,
@Embedded(prefix = "other_")
val other_reference: Reference
)

与@查询一起使用,例如:-

@Query("SELECT article.*, " +
/* as prefix = "main_" has been used then rename output columns accordingly */
"m.id AS main_id, m.other_data AS main_other_data, " +
/* as prefix = "other_" has been used then rename output columns accordingly */
"o.id AS other_id, o.other_data AS other_other_data " +
"FROM article " +
"JOIN reference AS m /*<<<<< to disambiguate column names */ ON main_reference_id = m.id " +
"JOIN reference AS o /*<<<<< to disambiguate column names */ ON main_reference_id = o.id ")
abstract fun getArticleFullAlternative(): List<ArticleFullAlternative>

在Activity中使用的一个例子可以是:-

for(afa: ArticleFullAlternative in articleAndReferenceDao.getArticleFullAlternative()) {
Log.d("ALTARTICLEINFO"," Article is ${afa.article.content} ID is ${afa.article.id} " +
"ntMain Reference is ${afa.main_reference.other_data} ID is ${afa.main_reference.id}" +
"ntSecondary  Reference is ${afa.other_reference.other_data} ID is ${afa.other_reference.id}")
}
  • 这产生完全相同的输出

相关内容

  • 没有找到相关文章