Android 房间属于(一对一)关系
Android Room belongsTo (one to one) relationship
假设我有这样一篇文章 table:
Article {
id;
title;
content;
main_reference_id;
secondary_reference_id;
}
还有一个参考 table 有点像这样:
Reference {
id;
other_reference_related_columns...;
}
现在我想在获取文章的同时还使用另一个 POJO 获取 main reference
、secondary reference
:
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
注释。
N.B.: 我是Laravel/Eloquent出身,所以比较熟悉这些belongsTo
, hasOne
, hasMany
, belongsToMany
,等等关系类型。
谢谢。
But I'm not sure what I wrote is the right usage of @Relation annotation or not.
是的,没关系。
这是一个工作示例。这显示了 ArticleFull
POJO 的使用:-
首先是实体(表):-
参考:-
@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 在外键中是可选的。
一个@Daoclass(抽象class而不是接口,抽象class更通用)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 class 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
}
}
}
最后是一些 Activity 代码,注意为了方便和简洁起见,使用 .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} " +
"\n\tMain Reference is ${d.main_reference.other_data} ID is ${d.main_reference.id}" +
"\n\tSecondary 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,例如:-
@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} " +
"\n\tMain Reference is ${afa.main_reference.other_data} ID is ${afa.main_reference.id}" +
"\n\tSecondary Reference is ${afa.other_reference.other_data} ID is ${afa.other_reference.id}")
}
- 这会产生完全相同的输出
假设我有这样一篇文章 table:
Article {
id;
title;
content;
main_reference_id;
secondary_reference_id;
}
还有一个参考 table 有点像这样:
Reference {
id;
other_reference_related_columns...;
}
现在我想在获取文章的同时还使用另一个 POJO 获取 main reference
、secondary reference
:
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
注释。
N.B.: 我是Laravel/Eloquent出身,所以比较熟悉这些belongsTo
, hasOne
, hasMany
, belongsToMany
,等等关系类型。
谢谢。
But I'm not sure what I wrote is the right usage of @Relation annotation or not.
是的,没关系。
这是一个工作示例。这显示了 ArticleFull
POJO 的使用:-
首先是实体(表):-
参考:-
@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 在外键中是可选的。
一个@Daoclass(抽象class而不是接口,抽象class更通用)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 class 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
}
}
}
最后是一些 Activity 代码,注意为了方便和简洁起见,使用 .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} " +
"\n\tMain Reference is ${d.main_reference.other_data} ID is ${d.main_reference.id}" +
"\n\tSecondary 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,例如:-
@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} " +
"\n\tMain Reference is ${afa.main_reference.other_data} ID is ${afa.main_reference.id}" +
"\n\tSecondary Reference is ${afa.other_reference.other_data} ID is ${afa.other_reference.id}")
}
- 这会产生完全相同的输出