SQLite הוא מסד נתונים יחסי, ולכן אפשר להגדיר קשרי גומלין בין ישויות. עם זאת, בעוד שרוב ספריות המיפוי של אובייקטים יחסיים מאפשרות לאובייקטים של ישויות להפנות זה לזה, ב-Room אסור לעשות זאת במפורש. למידע על הסיבות הטכניות להחלטה הזו, אפשר לעיין במאמר למה לא ניתן להשתמש בהפניות לאובייקטים ב-Room.
סוגי היחסים
ב-Room יש תמיכה בסוגי היחסים הבאים:
- אחד לאחד: מייצג קשר שבו ישות אחת קשורה לישות אחת אחרת.
- אחד לרבים: מייצג קשר שבו ישות אחת יכולה להיות קשורה למספר ישויות מסוג אחר.
- רבים לרבים: מייצג קשר שבו כמה ישויות מסוג אחד יכולות להיות קשורות לכמה ישויות מסוג אחר. בדרך כלל נדרשת טבלת צומת.
- יחסי עץ (באמצעות אובייקטים מוטמעים): מייצגים קשר שבו ישות מכילה ישות אחרת כשדה, והישות המשולבת הזו יכולה להכיל ישויות נוספות. לשם כך משתמשים בהערה
@Embedded
.
בחירה בין שתי גישות
ב-Room יש שתי דרכים להגדיר קשר בין ישויות ולבצע שאילתות לגביו. אפשר להשתמש באפשרויות הבאות:
- סוג נתונים ביניים עם אובייקטים מוטמעים, או
- שיטת שאילתה יחסית עם סוג החזרה של מפה מרובה.
אם אין לכם סיבה ספציפית להשתמש בסוגי נתונים ביניים, מומלץ להשתמש בגישה של סוג ההחזרה multimap. מידע נוסף על הגישה הזו זמין במאמר החזרת מפה מרובה.
הגישה של סיווג נתונים ברמה בינונית מאפשרת להימנע מכתיבת שאילתות SQL מורכבות, אבל היא עלולה גם להוביל לעלייה ברמת המורכבות של הקוד כי היא דורשת סיווגים נוספים של נתונים. בקצרה, הגישה של סוג ההחזרה multimap דורשת יותר עבודה משאילתות ה-SQL, והגישה של סוג הנתונים הביניים דורשת יותר עבודה מהקוד.
שימוש בגישה של סיווג נתונים ביניים
בגישה של סיווג נתונים ביניים, מגדירים סיווג נתונים שמתאר את הקשר בין ישויות המרחבים המשותפים. סיווג הנתונים הזה מכיל את ההתאמות בין מכונות של ישות אחת למכונות של ישות אחרת כאובייקטים מוטמעים. לאחר מכן, שיטות השאילתות יוכלו להחזיר מופעים של סוג הנתונים הזה לשימוש באפליקציה.
לדוגמה, אפשר להגדיר סיווג נתונים 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;
}
שימוש בגישה של סוגים מרובים של מפות משנה
בגישה של סוג ההחזרה multimap, אין צורך להגדיר עוד שיעורי נתונים. במקום זאת, מגדירים לסוג ההחזרה של השיטה את הערך multimap על סמך מבנה המפה הרצוי, ומגדירים את הקשר בין הישויות ישירות בשאילתת ה-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 (Android Dev Summit 2019)