ऑब्जेक्ट के बीच संबंध के टाइप चुनना

SQLite एक रिलेशनल डेटाबेस है. इसलिए, इसमें इकाइयों के बीच संबंध तय किए जा सकते हैं. हालांकि, ज़्यादातर ऑब्जेक्ट-रिलेशनल मैपिंग लाइब्रेरी, इकाई ऑब्जेक्ट को एक-दूसरे को रेफ़रंस करने की अनुमति देती हैं. वहीं, Room इस पर साफ़ तौर पर पाबंदी लगाता है. इस फ़ैसले के पीछे की तकनीकी वजह जानने के लिए, जानें कि Room, ऑब्जेक्ट रेफ़रंस की अनुमति क्यों नहीं देता लेख पढ़ें.

अलग-अलग तरह के संबंध

Room में, इस तरह के संबंध इस्तेमाल किए जा सकते हैं:

  • वन-टू-वन: यह एक ऐसा रिलेशनशिप होता है जिसमें एक इकाई, दूसरी इकाई से जुड़ी होती है.
  • वन-टू-मेनी: यह एक ऐसा संबंध दिखाता है जिसमें एक इकाई, किसी अन्य टाइप की कई इकाइयों से जुड़ी हो सकती है.
  • कई से कई: यह एक ऐसा संबंध होता है जिसमें एक टाइप की कई इकाइयां, दूसरे टाइप की कई इकाइयों से जुड़ी हो सकती हैं. इसके लिए, आम तौर पर जंक्शन टेबल की ज़रूरत होती है.
  • एम्बेड किए गए ऑब्जेक्ट का इस्तेमाल करके नेस्ट किए गए रिलेशनशिप: यह एक ऐसे रिलेशनशिप को दिखाता है जिसमें एक इकाई में दूसरी इकाई को प्रॉपर्टी के तौर पर शामिल किया जाता है. साथ ही, इस नेस्ट की गई इकाई में अन्य इकाइयों को भी शामिल किया जा सकता है. यह @Embedded एनोटेशन का इस्तेमाल करता है.

दो में से कोई एक तरीका चुनें

Room में, इकाइयों के बीच संबंध को तय करने और क्वेरी करने के दो तरीके हैं. इनमें से किसी एक का इस्तेमाल किया जा सकता है:

  • एम्बेड किए गए ऑब्जेक्ट वाली इंटरमीडिएट डेटा क्लास या
  • यह एक रिलेशनल क्वेरी फ़ंक्शन है, जो मल्टीमैप रिटर्न टाइप की वैल्यू दिखाता है.

अगर आपके पास इंटरमीडिएट डेटा क्लास इस्तेमाल करने की कोई खास वजह नहीं है, तो हमारा सुझाव है कि मल्टीमैप रिटर्न टाइप का इस्तेमाल करें. इस तरीके के बारे में ज़्यादा जानने के लिए, मल्टीमैप वापस पाना लेख पढ़ें.

इंटरमीडिएट डेटा क्लास का इस्तेमाल करने से, आपको जटिल एसक्यूएल क्वेरी लिखने की ज़रूरत नहीं पड़ती. हालांकि, इससे कोड की जटिलता भी बढ़ सकती है, क्योंकि इसके लिए अतिरिक्त डेटा क्लास की ज़रूरत होती है. संक्षेप में, मल्टीमैप रिटर्न टाइप के तरीके में, आपकी SQL क्वेरी को ज़्यादा काम करना पड़ता है. वहीं, इंटरमीडिएट डेटा क्लास के तरीके में, आपके कोड को ज़्यादा काम करना पड़ता है.

इंटरमीडिएट डेटा क्लास का इस्तेमाल करना

इंटरमीडिएट डेटा क्लास के तरीके में, एक ऐसी डेटा क्लास तय की जाती है जो आपकी रूम इकाइयों के बीच के संबंध को मॉडल करती है. इस डेटा क्लास में, एक इकाई के इंस्टेंस और दूसरी इकाई के इंस्टेंस के बीच की जोड़ियां, एम्बेड किए गए ऑब्जेक्ट के तौर पर सेव होती हैं. इसके बाद, क्वेरी फ़ंक्शन, इस डेटा क्लास के इंस्टेंस दिखा सकते हैं, ताकि उन्हें आपके ऐप्लिकेशन में इस्तेमाल किया जा सके.

उदाहरण के लिए, लाइब्रेरी के उन उपयोगकर्ताओं को दिखाने के लिए 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 रिटर्न टाइप तय करना होता है. यह रिटर्न टाइप, आपको जिस मैप स्ट्रक्चर की ज़रूरत है उसके हिसाब से तय किया जाता है. साथ ही, आपको अपनी SQL क्वेरी में सीधे तौर पर अपनी इकाइयों के बीच संबंध तय करना होता है.

उदाहरण के लिए, यहां दिया गया क्वेरी फ़ंक्शन, User और Book इंस्टेंस की मैपिंग दिखाता है. इससे, लाइब्रेरी के उन उपयोगकर्ताओं के बारे में पता चलता है जिन्होंने कुछ किताबें ली हैं:

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

मल्टीमैप रिटर्न टाइप की मदद से, एक-से-एक संबंधों के बारे में भी क्वेरी की जा सकती है. इनमें किसी दूसरी इकाई को शामिल नहीं किया जाता. नीचे दिया गया क्वेरी फ़ंक्शन, 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 प्रॉपर्टी के कंपोज़िशन को दिखाती है. कंपोज़ किए गए कॉलम को टेबल में अलग-अलग सेव करने के लिए, User क्लास में मौजूद Address प्रॉपर्टी को @Embedded के साथ एनोटेट करें. यहां दिए गए कोड स्निपेट में, इस सेटअप को दिखाया गया है:

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, एम्बेड किए गए ऑब्जेक्ट में हर कॉलम के नाम की शुरुआत में दी गई वैल्यू जोड़ता है.