تحديد العلاقات بين العناصر

نظرًا لأن SQLite هي قاعدة بيانات ارتباطية، يمكنك تحديد العلاقات بين والكيانات. ولكن في حين أن معظم مكتبات التعيينات العلائقية للكائنات تتيح التي تشير إلى بعضها بعضًا، فإن الغرفة تحظر ذلك صراحةً. للتعرّف على الأسباب الفنية وراء هذا القرار، راجِع فهم أسباب عدم السماح بمراجع الكائنات

هناك نهجان محتملان

في الغرفة، هناك طريقتان لتحديد العلاقة بين الكيانات والاستعلام عنها: باستخدام إما فئة بيانات متوسطة الكائنات المضمنة أو طريقة طلب بحث ارتباطي مع عرض خرائط متعددة الكتابة.

فئة البيانات المتوسطة

في منهج فئة البيانات المتوسطة، يمكنك تحديد فئة بيانات تُنشئ نموذجًا العلاقة بين كيانات غرفتك. تحتفظ فئة البيانات هذه بعمليات الإقران بين مثيلات أحد الكيانات ومثيلات كيان آخر باعتبارها مضمّنة . يمكن لطرق الاستعلام بعد ذلك إرجاع مثيلات هذا فئة البيانات لاستخدامها في تطبيقك.

على سبيل المثال، يمكنك تحديد فئة بيانات 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();

اختيار الأسلوب

تتوافق هذه الغرفة مع هذَين الأسلوبين، وبالتالي يمكنكم استخدام على أفضل نهج لتطبيقك. يناقش هذا القسم بعض الأسباب التي قد تجعلك تفضل أحدهما أو الآخر.

يتيح لك نهج فئة البيانات المتوسطة تجنب كتابة لغة SQL معقدة ولكن يمكن أن يؤدي أيضًا إلى زيادة تعقيد الرمز بسبب وفئات البيانات الإضافية التي تتطلبها. باختصار، يختلف نوع إرجاع الخرائط المتعددة استعلامات SQL الخاصة بك للقيام بمزيد من العمل، كما أن البيانات الوسيطة نهج الفئة يتطلب رمزك للقيام بالمزيد من العمل.

إذا لم يكن لديك سبب محدد لاستخدام فئات البيانات المتوسطة، ننصح باستخدام منهج نوع الرجوع للخرائط المتعددة. لمزيد من المعلومات حول هذه الطريقة، راجع عرض الخريطة المتعددة.

يوضح الجزء المتبقي من هذا الدليل كيفية تحديد العلاقات باستخدام لنهج فئة البيانات المتوسطة.

إنشاء عناصر مضمَّنة

قد ترغب أحيانًا في التعبير عن كيان أو كائن بيانات كامل متماسك في منطق قاعدة البيانات الخاصة بك، حتى إذا كان الكائن يحتوي على عدة الحقول. في هذه الحالات، يمكنك استخدام دالة الرسم @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 الموقع. بعد ذلك، تضيف الغرفة القيمة المتوفّرة إلى بداية كل عمود. الاسم في الكائن المضمّن.

تعريف العلاقات الفردية

العلاقة الفردية بين كيانين هي علاقة يتبادل فيها كل كيان مثيل الكيان الأصلي يتجاوب بالضبط مع مثيل واحد من العنصر التابع الكيان، والعكس صحيح أيضًا.

على سبيل المثال، يمكنك استخدام تطبيق لبث الموسيقى يحتوي على مكتبة من والأغاني التي يملكونها. لكل مستخدم مكتبة واحدة فقط، ولكل مكتبة مع مستخدم واحد بالضبط. لذلك، هناك خطة فردية العلاقة بين الكيان User والكيان Library.

لتحديد العلاقة واحد لواحد، أنشئ أولاً فئة لكل من الاثنين والكيانات. يجب أن يكون أحد الكيانات تضمين متغيرًا يمثل مرجعًا للمفتاح الأساسي للكيان الآخر.

Kotlin

@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)

@Entity
data class Library(
    @PrimaryKey val libraryId: Long,
    val userOwnerId: Long
)

Java

@Entity
public class User {
    @PrimaryKey public long userId;
    public String name;
    public int age;
}

@Entity
public class Library {
    @PrimaryKey public long libraryId;
    public long userOwnerId;
}

لطلب البحث في قائمة المستخدمين والمكتبات المقابلة، يجب أولاً لإنشاء نموذج للعلاقة واحد لواحد بين الكيانين. للقيام بذلك، قم بإنشاء فئة بيانات جديدة حيث يحتوي كل مثيل على مثيل للكيان الأصلي المثيل المقابل للكيان الفرعي. إضافة @Relation تعليق توضيحي لمثيل الكيان الفرعي، مع ضبط parentColumn على اسم عمود المفتاح الأساسي للكيان الأصلي وentityColumn يتم تعيينها على اسم عمود الكيان الفرعي الذي يشير إلى العنصر الرئيسي المفتاح الأساسي للكيان.

Kotlin

data class UserAndLibrary(
    @Embedded val user: User,
    @Relation(
         parentColumn = "userId",
         entityColumn = "userOwnerId"
    )
    val library: Library
)

Java

public class UserAndLibrary {
    @Embedded public User user;
    @Relation(
         parentColumn = "userId",
         entityColumn = "userOwnerId"
    )
    public Library library;
}

أخيرًا، أضف طريقة إلى فئة DAO تعرض جميع مثيلات البيانات فئة تربط بين الكيان الأصلي والكيان الفرعي. تتطلب هذه الطريقة مساحة لتشغيل طلبَي بحث، لذا أضِف التعليق التوضيحي @Transaction إلى هذا بحيث يتم تنفيذ العملية بأكملها على نحو ذري.

Kotlin

@Transaction
@Query("SELECT * FROM User")
fun getUsersAndLibraries(): List<UserAndLibrary>

Java

@Transaction
@Query("SELECT * FROM User")
public List<UserAndLibrary> getUsersAndLibraries();

تعريف العلاقات من واحد إلى متعدد

علاقة واحد إلى متعدد بين كيانين هي علاقة يتبادل فيها كل كيان لا يتجاوب مثيل الكيان الأصلي مع أي مثيل أو أكثر من تكرار الكيان التابع كيان، ولكن يمكن لكل مثيل في الكيان الفرعي أن يتوافق مع كيان واحد بالضبط للكيان الأصلي.

في مثال تطبيق بث الموسيقى، لنفترض أن المستخدم لديه القدرة على تنظيم أغانيهم في قوائم تشغيل. يمكن لكل مستخدم إنشاء قدر ما يشاء من قوائم التشغيل، ولكن كل قائمة تشغيل ينشئها مستخدم واحد فقط لذلك، هناك علاقة واحد إلى متعدد بين الكيان User والكيان Playlist.

لتعريف علاقة واحد إلى متعدد، قم أولاً بإنشاء فئة للكيانين. وكما هو الحال في العلاقة واحد لواحد، يجب أن يتضمن الكيان الفرعي متغيرًا هو مرجع إلى المفتاح الأساسي للكيان الأصلي.

Kotlin

@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val userCreatorId: Long,
    val playlistName: String
)

Java

@Entity
public class User {
    @PrimaryKey public long userId;
    public String name;
    public int age;
}

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public long userCreatorId;
    public String playlistName;
}

لطلب البحث عن قائمة المستخدمين وقوائم التشغيل المقابلة، يجب أولاً لإنشاء نموذج لعلاقة واحد إلى متعدد بين الكيانين. للقيام بذلك، قم بإنشاء فئة بيانات جديدة حيث يحتوي كل مثيل على مثيل للكيان الأصلي قائمة بجميع مثيلات الكيانات الفرعية المقابلة. إضافة @Relation تعليق توضيحي لمثيل الكيان الفرعي، مع ضبط parentColumn على اسم عمود المفتاح الأساسي للكيان الأصلي وentityColumn يتم تعيينها على اسم عمود الكيان الفرعي الذي يشير إلى العنصر الرئيسي المفتاح الأساسي للكيان.

Kotlin

data class UserWithPlaylists(
    @Embedded val user: User,
    @Relation(
          parentColumn = "userId",
          entityColumn = "userCreatorId"
    )
    val playlists: List<Playlist>
)

Java

public class UserWithPlaylists {
    @Embedded public User user;
    @Relation(
         parentColumn = "userId",
         entityColumn = "userCreatorId"
    )
    public List<Playlist> playlists;
}

أخيرًا، أضف طريقة إلى فئة DAO تعرض جميع مثيلات البيانات فئة تربط بين الكيان الأصلي والكيان الفرعي. تتطلب هذه الطريقة مساحة لتشغيل طلبَي بحث، لذا أضِف التعليق التوضيحي @Transaction إلى هذا بحيث يتم تنفيذ العملية بأكملها على نحو ذري.

Kotlin

@Transaction
@Query("SELECT * FROM User")
fun getUsersWithPlaylists(): List<UserWithPlaylists>

Java

@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylists> getUsersWithPlaylists();

تعريف علاقات كثير إلى متعدد

إنّ علاقة "عدّة إلى الكثير" بين كيانَين هي علاقة يتنافس فيها كلّ منهما لا يتجاوب مثيل الكيان الأصلي مع أي مثيل أو أكثر من تكرار الكيان التابع الكيان، والعكس صحيح أيضًا.

في مثال تطبيق بث الموسيقى، ضع في اعتبارك الأغاني في قوائم التشغيل التي يحددها المستخدم. يمكن أن تتضمن كل قائمة تشغيل عدة أغانٍ، ويمكن أن تكون كل أغنية جزءًا من العديد منها قوائم تشغيل مختلفة. ومن ثم، فإن هناك علاقة أطراف متعددة بين الكيان Playlist والكيان Song.

لتحديد علاقة الكثير إلى كثير، أنشئ أولاً فئة لكل من اثنين والكيانات. علاقات كثير إلى متعدد تختلف عن أنواع العلاقات الأخرى لأنه لا الإشارة إلى الكيان الأصلي في الكيان الفرعي. بدلاً من ذلك، قم بإنشاء جزء ثالث لتمثيل كيان رابطي، أو كيان اتحادي ، بين الكيانين. يجب أن يحتوي جدول المراجع المتداخلة على أعمدة المفتاح الأساسي من كل كيان في علاقة "متعدد إلى متعدد" ممثلة في تقديمه. في هذا المثال، يتجاوب كل صف في جدول المراجع المتداخلة مع إقران مثيل Playlist ومثيل Song حيث تمت الإشارة إلى الأغنية المضمّنة في قائمة التشغيل المرجعية.

Kotlin

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public String playlistName;
}

@Entity
public class Song {
    @PrimaryKey public long songId;
    public String songName;
    public String artist;
}

@Entity(primaryKeys = {"playlistId", "songId"})
public class PlaylistSongCrossRef {
    public long playlistId;
    public long songId;
}

تعتمد الخطوة التالية على الطريقة التي تريد بها الاستعلام عن هذه الكيانات ذات الصلة.

  • إذا أردت إجراء طلب بحث عن قوائم التشغيل وقائمة بالأغاني المناسبة لكل قائمة تشغيل، أنشِئ فئة بيانات جديدة تحتوي على عنصر Playlist واحد. وقائمة بجميع عناصر Song التي تتضمّنها قائمة التشغيل
  • إذا أردت إجراء طلب بحث عن الأغاني وقائمة بقوائم التشغيل المناسبة لها لكل منها، إنشاء فئة بيانات جديدة تحتوي على كائن Song واحد وقائمة لكل عناصر Playlist التي تتضمّن الأغنية

وفي كلتا الحالتين، قم بنموذج العلاقة بين الكيانات باستخدام السمة associateBy في التعليق التوضيحي @Relation في كل من هذه الفئات لتحديد كيان تداخل المراجع الذي يوفر العلاقة بين الكيان Playlist والكيان Song.

Kotlin

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)

data class SongWithPlaylists(
    @Embedded val song: Song,
    @Relation(
         parentColumn = "songId",
         entityColumn = "playlistId",
         associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val playlists: List<Playlist>
)

Java

public class PlaylistWithSongs {
    @Embedded public Playlist playlist;
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Song> songs;
}

public class SongWithPlaylists {
    @Embedded public Song song;
    @Relation(
         parentColumn = "songId",
         entityColumn = "playlistId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Playlist> playlists;
}

أخيرًا، أضف طريقة إلى فئة DAO للكشف عن وظيفة الاستعلام احتياجاتك.

  • getPlaylistsWithSongs: تبحث هذه الطريقة في قاعدة البيانات وتُرجع جميع الكائنات PlaylistWithSongs الناتجة.
  • getSongsWithPlaylists: تبحث هذه الطريقة في قاعدة البيانات وتُرجع جميع الكائنات SongWithPlaylists الناتجة.

تتطلب كل من هذه الطرق مساحة لتشغيل استعلامين، لذا أضف @Transaction إضافة تعليق توضيحي لكلا الطريقتين بحيث يمكن للجميع يتم تنفيذ العملية بشكل ذري.

Kotlin

@Transaction
@Query("SELECT * FROM Playlist")
fun getPlaylistsWithSongs(): List<PlaylistWithSongs>

@Transaction
@Query("SELECT * FROM Song")
fun getSongsWithPlaylists(): List<SongWithPlaylists>

Java

@Transaction
@Query("SELECT * FROM Playlist")
public List<PlaylistWithSongs> getPlaylistsWithSongs();

@Transaction
@Query("SELECT * FROM Song")
public List<SongWithPlaylists> getSongsWithPlaylists();

تحديد العلاقات المتداخلة

في بعض الأحيان، قد تحتاج إلى الاستعلام عن مجموعة من ثلاثة جداول أو أكثر جميعها مرتبطين ببعضها البعض. وفي هذه الحالة، يمكنك تحديد العلاقات المتداخلة. بين الجداول.

لنفترض أنك في مثال تطبيق بث الموسيقى، تريد الاستعلام عن جميع والمستخدمين وقوائم التشغيل لكل مستخدم وجميع الأغاني في كل قائمة تشغيل لكل مستخدم. تقع على عاتق المستخدمين علاقة واحد لمتعدد مع وقوائم التشغيل وقوائم التشغيل القائمة على علاقة متعددة إلى متعدد مع أغانٍ. يوضح مثال الرمز التالي الفئات التي تمثل هذه الكيانات بالإضافة إلى جدول تداخل المراجع لعلاقة "متعدد إلى متعدد" بين قوائم التشغيل والأغاني:

Kotlin

@Entity
data class User(
    @PrimaryKey val userId: Long,
    val name: String,
    val age: Int
)

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val userCreatorId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class User {
    @PrimaryKey public long userId;
    public String name;
    public int age;
}

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public long userCreatorId;
    public String playlistName;
}
@Entity
public class Song {
    @PrimaryKey public long songId;
    public String songName;
    public String artist;
}

@Entity(primaryKeys = {"playlistId", "songId"})
public class PlaylistSongCrossRef {
    public long playlistId;
    public long songId;
}

أولاً، ضع نموذجًا للعلاقة بين جدولين في مجموعتك كما عادةً، باستخدام فئة بيانات التعليق التوضيحي @Relation. تشير رسالة الأشكال البيانية يوضح المثال التالي فئة PlaylistWithSongs التي تمثل نموذجًا متعددًا لمتعدد. العلاقة بين فئة الكيان Playlist وفئة الكيان Song:

Kotlin

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)

Java

public class PlaylistWithSongs {
    @Embedded public Playlist playlist;
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = Junction(PlaylistSongCrossRef.class)
    )
    public List<Song> songs;
}

بعد تحديد فئة بيانات تمثل هذه العلاقة، أنشئ فئة أخرى فئة البيانات التي تصمم العلاقة بين جدول آخر من مجموعتك فئة العلاقة الأولى، "تضمين" العلاقة الحالية داخل نظام إدارة واحد. يوضح المثال التالي فئة UserWithPlaylistsAndSongs التي تمثل علاقة واحد إلى متعدد بين فئة الكيان User فئة العلاقة PlaylistWithSongs:

Kotlin

data class UserWithPlaylistsAndSongs(
    @Embedded val user: User
    @Relation(
        entity = Playlist::class,
        parentColumn = "userId",
        entityColumn = "userCreatorId"
    )
    val playlists: List<PlaylistWithSongs>
)

Java

public class UserWithPlaylistsAndSongs {
    @Embedded public User user;
    @Relation(
        entity = Playlist.class,
        parentColumn = "userId",
        entityColumn = "userCreatorId"
    )
    public List<PlaylistWithSongs> playlists;
}

تضع الفئة UserWithPlaylistsAndSongs نماذج للعلاقات بشكل غير مباشر. بين جميع فئات الكيانات الثلاث: User وPlaylist وSong. هذا هو كما هو موضح في الشكل 1.

تضع UserWithplaylistsAndSongs نموذجًا للعلاقة بين المستخدم
  قائمة التشغيل PlayWithSongs التي تحدّد بدورها العلاقة بين قائمة التشغيل
  والأغنية.

الشكل 1. مخطط لفئات العلاقات في مثال على تطبيق بث الموسيقى.

إذا كان هناك أي جداول أخرى في مجموعتك، فأنشئ فئة لنمذجة العلاقة بين كل جدول متبقٍ وفئة العلاقة التي تُنشئها العلاقات بين جميع الجداول السابقة. يؤدي هذا إلى إنشاء سلسلة من والعلاقات بين جميع الجداول التي تريد الاستعلام عنها.

وأخيرًا، أضف طريقة إلى فئة DAO للكشف عن وظيفة الاستعلام التي الذي يحتاجه تطبيقك. تتطلب هذه الطريقة مساحة لتشغيل استعلامات متعددة، لذا أضف التعليق التوضيحي @Transaction بحيث يتم تنفيذ العملية بأكملها على نحو ذري:

Kotlin

@Transaction
@Query("SELECT * FROM User")
fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>

Java

@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();

مصادر إضافية

لمزيد من المعلومات حول تعريف العلاقات بين الكيانات في الغرفة، راجِع قسم ومتابعة الموارد الإضافية.

نماذج

الفيديوهات

المدوّنات