由於 SQLite 是關聯資料庫,因此您可以定義實體之間的關係。儘管大多數的物件關聯對應程式庫允許實體物件互相參照,但 Room 明確禁止這種做法。如要瞭解這項決策背後的技術原因,請參閱「瞭解 Room 不允許物件參照的原因」。
關係類型
Room 支援下列關係類型:
- 一對一:代表單一實體與另一個單一實體的關係。
- 一對多:代表單一實體可與另一個類型的多個實體建立關聯。
- 多對多:代表一種關係,其中一個類型的多個實體可與另一個類型的多個實體相關聯。這通常需要轉接資料表。
- 巢狀關係 (使用內嵌物件):代表實體包含另一個實體做為欄位的關係,而這個巢狀實體可以進一步包含其他實體。這會使用
@Embedded
註解。
選擇兩種方法
在 Room 中,您可以透過兩種方式定義及查詢實體之間的關係。您可以使用下列任一方法:
- 含有嵌入物件的中繼資料類別,或
- 搭配多重對應傳回類型的關聯查詢方法。
如果沒有需要採用中繼資料類別的特定理由,建議您採用多重對應傳回類型的方法。如要進一步瞭解這種方法,請參閱「傳回多重對應」一文。
採用中繼資料類別的方法可讓您避免編寫複雜的 SQL 查詢,但也可能因為需要額外的資料類別,導致程式碼更加複雜。簡單來說,採用多重對應傳回類型的方法需要透過 SQL 查詢完成更多作業,而採用中繼資料類別的方法則需透過程式碼完成更多作業。
使用中繼資料類別方法
如要採用中繼資料類別的方式,您必須定義資料類別,以建立 Room 實體之間的關係模型。這個資料類別會保留其中一個實體的例項與另一個實體的例項之間的配對連線,做為內嵌物件。這樣一來,您的查詢方法就能傳回這個資料類別的例項,以便用於應用程式。
舉例來說,您可以定義 UserBook
資料類別來代表曾借出特定圖書的圖書館使用者,並定義查詢方法,從資料庫擷取 UserBook
例項清單:
Kotlin
@Dao
interface UserBookDao {
@Query(
"SELECT user.name AS userName, book.name AS bookName " +
"FROM user, book " +
"WHERE user.id = book.user_id"
)
fun loadUserAndBookNames(): LiveData<List<UserBook>>
}
data class UserBook(val userName: String?, val bookName: String?)
Java
@Dao
public interface UserBookDao {
@Query("SELECT user.name AS userName, book.name AS bookName " +
"FROM user, book " +
"WHERE user.id = book.user_id")
public LiveData<List<UserBook>> loadUserAndBookNames();
}
public class UserBook {
public String userName;
public String bookName;
}
使用多重對應傳回類型
如要採用多重對應傳回類型的方式,您不需要定義任何其他資料類別,而是根據您想要的對應結構,為方法定義多重對應傳回類型,並且直接在 SQL 查詢中定義實體之間的關係。
舉例來說,以下查詢方法會傳回 User
和 Book
例項的對應關係,以代表曾借出特定圖書的圖書館使用者:
Kotlin
@Query(
"SELECT * FROM user" +
"JOIN book ON user.id = book.user_id"
)
fun loadUserAndBookNames(): Map<User, List<Book>>
Java
@Query(
"SELECT * FROM user" +
"JOIN book ON user.id = book.user_id"
)
public Map<User, List<Book>> loadUserAndBookNames();
建立內嵌物件
有時候,即使物件含有多個欄位,您仍會希望在資料庫邏輯中將實體或資料物件表示為一個凝聚的整體。如果是這種情況,您可以使用 @Embedded
註解來代表要在資料表內細分為多個子欄位的物件。這樣一來,您就可以使用查詢其他個別資料欄的相同方式,查詢內嵌的欄位。
舉例來說,User
類別可以包含 Address
類型的欄位,該欄位代表名為 street
、city
、state
和 postCode
的欄位組合。如要在資料表中分別儲存組成的資料欄,請加入 Address
欄位。這應該會顯示在 User
類別中,並加上 @Embedded
註解。下列程式碼片段說明這項程序:
Kotlin
data class Address(
val street: String?,
val state: String?,
val city: String?,
@ColumnInfo(name = "post_code") val postCode: Int
)
@Entity
data class User(
@PrimaryKey val id: Int,
val firstName: String?,
@Embedded val address: Address?
)
Java
public class Address {
public String street;
public String state;
public String city;
@ColumnInfo(name = "post_code") public int postCode;
}
@Entity
public class User {
@PrimaryKey public int id;
public String firstName;
@Embedded public Address address;
}
這樣一來,代表 User
物件的資料表就會包含使用以下名稱的資料欄:id
、firstName
、street
、state
、city
和 post_code
。
如果實體含有多個相同類型的內嵌欄位,您可以設定 prefix
屬性,讓每個資料欄使用不重複的名稱。接著,Room 會將提供的值加到內嵌物件中每個資料欄名稱的開頭。
其他資源
如要進一步瞭解如何在 Room 中定義實體之間的關係,請參閱下列其他資源。
影片
- Room 最新消息 (2019 年 Android 開發人員高峰會)