الإشارة إلى البيانات المعقدة باستخدام الغرفة

توفّر الغرفة وظيفة للتحويل بين الأنواع الأساسية والنمطية، ولكنها لا تسمح بمراجع العناصر بين الكيانات. يشرح هذا المستند كيفية استخدام محولات الأنواع ولماذا لا تدعم الغرفة مراجع العناصر.

استخدام أدوات تحويل الأنواع

في بعض الأحيان، تحتاج إلى تطبيقك لتخزين نوع بيانات مخصص في عمود قاعدة بيانات واحد. يمكنك إتاحة الأنواع المخصّصة من خلال توفير برامج تحويل الأنواع، وهي طرق توضّح لغرفة كيفية تحويل الأنواع المخصّصة من الأنواع المعروفة التي يمكن أن تستمرّ إليها الغرفة ومنها. يمكنك تحديد منفِّذي الإحالات الناجحة باستخدام التعليق التوضيحي @TypeConverter.

فرضًا أنّك تحتاج إلى الاحتفاظ بمثيلات من Date في قاعدة بيانات الغرفة. لا يعرف Room كيفية الاحتفاظ بكائنات Date، لذلك تحتاج إلى تحديد أنواع المحوِّلات:

Kotlin

class Converters {
  @TypeConverter
  fun fromTimestamp(value: Long?): Date? {
    return value?.let { Date(it) }
  }

  @TypeConverter
  fun dateToTimestamp(date: Date?): Long? {
    return date?.time?.toLong()
  }
}

Java

public class Converters {
  @TypeConverter
  public static Date fromTimestamp(Long value) {
    return value == null ? null : new Date(value);
  }

  @TypeConverter
  public static Long dateToTimestamp(Date date) {
    return date == null ? null : date.getTime();
  }
}

يحدّد هذا المثال طريقتَي تحويل من النوع: الطريقة التي تحوِّل كائن Date إلى كائن Long والأخرى تنفّذ التحويل العكسي من Long إلى Date. ولأنّ الغرفة تعرف كيفية الاحتفاظ بكائنات Long، يمكنها استخدام برامج الإحالات الناجحة هذه للاحتفاظ بعناصر Date.

بعد ذلك، يمكنك إضافة التعليق التوضيحي @TypeConverters إلى فئة AppDatabase حتى تتعرّف الغرفة على فئة المحوِّل التي حددتها:

Kotlin

@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
  abstract fun userDao(): UserDao
}

Java

@Database(entities = {User.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
  public abstract UserDao userDao();
}

مع تحديد هذه الأنواع من المحولات، يمكنك استخدام النوع المخصص في الكيانات وقوائم DAO تمامًا كما تستخدم الأنواع الأساسية:

Kotlin

@Entity
data class User(private val birthday: Date?)

@Dao
interface UserDao {
  @Query("SELECT * FROM user WHERE birthday = :targetDate")
  fun findUsersBornOnDate(targetDate: Date): List<User>
}

Java

@Entity
public class User {
  private Date birthday;
}

@Dao
public interface UserDao {
  @Query("SELECT * FROM user WHERE birthday = :targetDate")
  List<User> findUsersBornOnDate(Date targetDate);
}

في هذا المثال، يمكن أن تستخدم Room محوِّل النوع المحدّد في كل مكان لأنك أضفت تعليقًا توضيحيًا إلى AppDatabase باستخدام @TypeConverters. مع ذلك، يمكنك أيضًا تحديد نطاق المحولين في أنواع المحولات إلى كيانات محدّدة أو قوائم DAO من خلال إضافة تعليقات توضيحية إلى فئتَي @Entity أو @Dao باستخدام @TypeConverters.

ضبط إعدادات محوّل نوع التحكّم

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

Kotlin

@ProvidedTypeConverter
class ExampleConverter {
  @TypeConverter
  fun StringToExample(string: String?): ExampleType? {
    ...
  }

  @TypeConverter
  fun ExampleToString(example: ExampleType?): String? {
    ...
  }
}

Java

@ProvidedTypeConverter
public class ExampleConverter {
  @TypeConverter
  public Example StringToExample(String string) {
    ...
  }

  @TypeConverter
  public String ExampleToString(Example example) {
    ...
  }
}

بعد ذلك، بالإضافة إلى تعريف فئة المحوّل في @TypeConverters، استخدِم الطريقة RoomDatabase.Builder.addTypeConverter() لتمرير مثيل من فئة المحوّل إلى أداة إنشاء RoomDatabase:

Kotlin

val db = Room.databaseBuilder(...)
  .addTypeConverter(exampleConverterInstance)
  .build()

Java

AppDatabase db = Room.databaseBuilder(...)
  .addTypeConverter(exampleConverterInstance)
  .build();

فهم سبب عدم سماح الغرفة بإشارات العناصر

نصيحة رئيسية: لا تسمح الغرفة بإشارات العناصر بين فئات الكيانات. بدلاً من ذلك، يجب عليك طلب البيانات التي يحتاجها تطبيقك صراحةً.

إن تعيين العلاقات من قاعدة بيانات بنموذج الكائن ذي الصلة هو ممارسة شائعة ويعمل بشكل جيد للغاية على الخادم. حتى عندما يقوم البرنامج بتحميل الحقول عند الوصول إليها، يظل الخادم يعمل بشكل جيد.

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

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

على سبيل المثال، جرِّب واجهة مستخدم تحمِّل قائمة بعناصر Book، على أن يحتوي كل كتاب على عنصر Author. قد تصمّم طلبات البحث في البداية لاستخدام التحميل الكسول للحصول على مثيلات Book التي تسترد المؤلف. يؤدي استرداد الحقل author إلى الاستعلام عن قاعدة البيانات. وبعد مرور بعض الوقت، تدرك أنك بحاجة إلى عرض اسم المؤلف في واجهة المستخدم الخاصة بتطبيقك أيضًا. يمكنك الوصول إلى هذا الاسم بسهولة كافية، كما هو موضّح في مقتطف الرمز التالي:

Kotlin

authorNameTextView.text = book.author.name

Java

authorNameTextView.setText(book.getAuthor().getName());

ومع ذلك، يبدو أنّ هذا التغيير غير سليم، يؤدي إلى إنشاء طلب بحث في جدول Author في سلسلة التعليمات الرئيسية.

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

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