เลือกประเภทความสัมพันธ์ระหว่างออบเจ็กต์

เนื่องจาก SQLite เป็นฐานข้อมูลเชิงสัมพันธ์ คุณจึงกำหนดความสัมพันธ์ระหว่างเอนทิตีได้ แต่ในขณะที่ไลบรารีการแมปออบเจ็กต์เชิงสัมพันธ์ส่วนใหญ่จะอนุญาตให้ออบเจ็กต์เอนทิตีอ้างอิงถึงกันได้ แต่ Room จะไม่อนุญาตอย่างชัดเจน หากต้องการดูเหตุผลทางเทคนิค เบื้องหลังการตัดสินใจนี้ โปรดดูทำความเข้าใจเหตุผลที่ Room ไม่อนุญาตการอ้างอิงออบเจ็กต์

ประเภทความสัมพันธ์

Room รองรับความสัมพันธ์ประเภทต่อไปนี้

  • แบบหนึ่งต่อหนึ่ง: แสดงถึงความสัมพันธ์ที่เอนทิตีเดียวมีความสัมพันธ์กับเอนทิตีเดียวอีกรายการ
  • แบบหนึ่งต่อหลาย: แสดงถึงความสัมพันธ์ที่เอนทิตีเดียวมีความสัมพันธ์กับเอนทิตีหลายรายการของอีกประเภทได้
  • แบบหลายต่อหลาย: แสดงถึงความสัมพันธ์ที่เอนทิตีหลายรายการของ ประเภทหนึ่งมีความสัมพันธ์กับเอนทิตีหลายรายการของอีกประเภทได้ ซึ่งมักจะต้องใช้ตารางเชื่อม
  • ความสัมพันธ์แบบซ้อนโดยใช้ออบเจ็กต์ที่ฝัง: แสดงถึง ความสัมพันธ์ที่เอนทิตีหนึ่งมีเอนทิตีอื่นเป็นพร็อพเพอร์ตี้ และ เอนทิตีที่ซ้อนนี้อาจมีเอนทิตีอื่นๆ เพิ่มเติมได้ ซึ่งใช้คำอธิบายประกอบ @Embedded

เลือกระหว่าง 2 วิธี

ใน Room คุณสามารถกำหนดและค้นหาความสัมพันธ์ระหว่างเอนทิตีได้ 2 วิธี โดยสามารถใช้วิธีใดวิธีหนึ่งต่อไปนี้

  • คลาสข้อมูลระดับกลางที่มีออบเจ็กต์ที่ฝัง หรือ
  • ฟังก์ชันการค้นหาเชิงสัมพันธ์ที่มีประเภทการแสดงผลเป็น multimap

หากไม่มีเหตุผลเฉพาะที่จะใช้คลาสข้อมูลระดับกลาง เราขอแนะนำให้ใช้วิธีประเภทการแสดงผลเป็น multimap ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีนี้ได้ที่ แสดงผล multimap

วิธีคลาสข้อมูลระดับกลางช่วยให้คุณไม่ต้องเขียนการค้นหา SQL ที่ซับซ้อน แต่ก็อาจทำให้โค้ดซับซ้อนมากขึ้นเนื่องจากต้องใช้คลาสข้อมูลเพิ่มเติม กล่าวโดยย่อคือ วิธีประเภทการแสดงผลเป็น multimap กำหนดให้การค้นหา SQL ของคุณทำงานมากขึ้น และวิธีคลาสข้อมูลระดับกลางกำหนดให้โค้ดของคุณทำงานมากขึ้น

ใช้วิธีคลาสข้อมูลระดับกลาง

ในวิธีคลาสข้อมูลระดับกลาง คุณจะกำหนดคลาสข้อมูลที่จำลองความสัมพันธ์ระหว่างเอนทิตี Room คลาสข้อมูลนี้จะเก็บการจับคู่ ระหว่างอินสแตนซ์ของเอนทิตีหนึ่งกับอินสแตนซ์ของเอนทิตีอื่นเป็น ออบเจ็กต์ที่ฝัง จากนั้นฟังก์ชันการค้นหาจะแสดงผลอินสแตนซ์ของคลาสข้อมูลนี้เพื่อใช้ในแอปได้

ตัวอย่างเช่น คุณสามารถกำหนดคลาสข้อมูล UserBook เพื่อแสดงผู้ใช้ห้องสมุดที่ยืมหนังสือเฉพาะ และกำหนดฟังก์ชันการค้นหาเพื่อดึงข้อมูลรายการอินสแตนซ์ UserBook จากฐานข้อมูลได้ดังนี้

@Dao
interface UserBookDao {
    @Query(
        """
        SELECT user.name AS userName, book.name AS bookName
        FROM user JOIN book ON user.id = book.user_id
        """
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}

data class UserBook(val userName: String, val bookName: String)

ใช้วิธีประเภทการแสดงผลเป็น multimap

ในวิธีประเภทการแสดงผลเป็น multimap คุณไม่จำเป็นต้องกำหนดคลาสข้อมูลเพิ่มเติม แต่คุณจะกำหนดประเภทการแสดงผลเป็น multimap สำหรับ ฟังก์ชันตามโครงสร้างแผนที่ที่ต้องการ และกำหนด ความสัมพันธ์ระหว่างเอนทิตีโดยตรงในการค้นหา SQL

ตัวอย่างเช่น ฟังก์ชันการค้นหาต่อไปนี้จะแสดงผลการแมปอินสแตนซ์ User และ Book เพื่อแสดงผู้ใช้ห้องสมุดที่ยืมหนังสือเฉพาะ

@Query(
    """
    SELECT *
    FROM user JOIN book ON user.id = book.user_id
    """
)
suspend fun loadUserAndBookNames(): Map<User, List<Book>>

นอกจากนี้ คุณยังค้นหาความสัมพันธ์แบบหนึ่งต่อหนึ่งที่ไม่มีเอนทิตีอื่นเกี่ยวข้องได้ด้วยประเภทการแสดงผลเป็น multimap ฟังก์ชันการค้นหาต่อไปนี้จะแสดงผลการแมป User และจำนวนหนังสือที่ผู้ใช้ยืมโดยใช้ @MapColumn คำอธิบายประกอบ:

@Query(
    """
    SELECT user.*, COUNT(book.id) AS book_count
    FROM user LEFT JOIN book ON user.id = book.user_id
    GROUP BY user.id
    """
)
suspend fun loadUserAndBookCount(): Map<User, @MapColumn(columnName = "book_count") Int>

สร้างออบเจ็กต์ที่ฝัง

บางครั้งคุณอาจต้องการแสดงเอนทิตีหรือออบเจ็กต์ข้อมูลเป็นหน่วยเดียวที่สอดคล้องกันในตรรกะฐานข้อมูล แม้ว่าออบเจ็กต์จะมีพร็อพเพอร์ตี้หลายรายการก็ตาม ในกรณีเช่นนี้ ให้ใช้คำอธิบายประกอบ @Embedded เพื่อแยกออบเจ็กต์ออกเป็น พร็อพเพอร์ตี้ย่อยภายในตาราง จากนั้นคุณจะค้นหาพร็อพเพอร์ตี้ที่ฝังได้เช่นเดียวกับคอลัมน์อื่นๆ

ตัวอย่างเช่น คลาส User อาจมีพร็อพเพอร์ตี้ Address ที่แสดงถึงการรวมพร็อพเพอร์ตี้ street, city, state และ postCode หากต้องการจัดเก็บคอลัมน์ที่รวมไว้แยกกันในตาราง ให้ใส่คำอธิบายประกอบ @Embedded ในพร็อพเพอร์ตี้ Address ในคลาส User ข้อมูลโค้ดต่อไปนี้แสดงการตั้งค่านี้

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?
)

จากนั้นตารางที่แสดงออบเจ็กต์ User จะมีคอลัมน์ที่มีชื่อต่อไปนี้ id, firstName, street, state, city และ post_code

หากเอนทิตีมีพร็อพเพอร์ตี้ที่ฝังหลายรายการที่มีประเภทเดียวกัน คุณสามารถทำให้ แต่ละคอลัมน์ไม่ซ้ำกันได้โดยการตั้งค่าพร็อพเพอร์ตี้ prefix จากนั้น Room จะเพิ่มค่าที่ระบุไว้ที่จุดเริ่มต้นของชื่อคอลัมน์แต่ละคอลัมน์ในออบเจ็กต์ที่ฝัง