7 نصائح مفيدة لاستخدام الغرفة

7 نصائح مفيدة لاستخدام الغرفة


الغرفة عبارة عن طبقة تجريدية أعلى سكليتي تسهل عملية التخزين. إذا كنت مستخدمًا جديدًا في Room (الغرفة) ، تحقق من هذه المقالة التمهيدية:


7 خطوات لاستخدام الغرفة. تجول لترحيل طلبك إلى Room

وفي هذه المقالة ، أود أن أشارككم بعض النصائح حول كيفية استخدام Room بأعلى كفاءة ممكنة.


1. قبل ملء قاعدة البيانات


هل تحتاج إلى إضافة البيانات الافتراضية إلى قاعدة البيانات الخاصة بك مباشرة بعد إنشائها أو في وقت الوصول الأول إليها؟ استخدام RoomDatabase # رد الاتصال . استدعاء الأسلوب addCallback عند إنشاء قاعدة البيانات الخاصة بك وتجاوز إما onCreate أو onOpen .


سيتم استدعاء onCreate عند إنشاء قاعدة البيانات لأول مرة ، مباشرة بعد إنشاء الجداول. يتم استدعاء onOpen عند فتح قاعدة البيانات. نظرًا لأن الوصول إلى DAO ممكن فقط بعد الانتهاء من هذه الطرق ، فإننا نقوم بإنشاء دفق جديد نحصل فيه على ارتباط بقاعدة البيانات ، ثم نحصل على DAO ونقوم بإدراج البيانات اللازمة.


 Room.databaseBuilder(context.applicationContext, DataDatabase::class.java, "Sample.db") // prepopulate the database after onCreate was called .addCallback(object : Callback() { override fun onCreate(db: SupportSQLiteDatabase) { super.onCreate(db) // moving to a new thread ioThread { getInstance(context).dataDao() .insert(PREPOPULATE_DATA) } } }) .build() 

انظر المثال الكامل هنا .


ملاحظة: إذا كنت تستخدم أسلوب ioThread وتعطل التطبيق في المرة الأولى التي تبدأ فيها ، بين إنشاء قاعدة البيانات والإدراج ، فلن يتم إدراج البيانات أبدًا.


2. استخدام قدرات الوراثة DAO


هل لديك جداول متعددة في قاعدة البيانات الخاصة بك ونسخ نفس أساليب الإدراج وتحديث وحذف ؟ تدعم DAO الوراثة ، لذلك قم بإنشاء فئة BaseDao<T> وحدد الطرق الشائعة الخاصة بك @Insert و @Update و @Delete . اسمح لكل DAO بتوسيع BaseDao وإضافة أساليب خاصة بكل منها.


 interface BaseDao<T> { @Insert fun insert(vararg obj: T) } @Dao abstract class DataDao : BaseDao<Data>() { @Query("SELECT * FROM Data") abstract fun getData(): List<Data> } 

انظر التفاصيل هنا .


يجب أن تكون DAOs واجهات أو فئات مجردة لأن Room يولد تنفيذها في وقت التحويل البرمجي ، بما في ذلك الأساليب من BaseDao .


3. تنفيذ الاستعلامات في المعاملات دون رمز boilerplate


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


 @Dao abstract class UserDao { @Transaction open fun updateData(users: List<User>) { deleteAllUsers() insertAll(users) } @Insert abstract fun insertAll(users: List<User>) @Query("DELETE FROM Users") abstract fun deleteAllUsers() } 

قد ترغب في استخدام التعليق التوضيحي @Transaction لأساليب @Transaction التي تستخدم عبارة select في الحالات:


  • عندما تكون نتيجة الاستعلام كبيرة جدًا. من خلال تقديم طلب في معاملة واحدة ، فإنك تضمن أنه في حالة عدم احتواء نتيجة الطلب في "جزء" واحد من المؤشر ، فلن يكون معطوبًا بسبب التغييرات في قاعدة البيانات بين التباديل بين المؤشر .
  • عندما تكون نتيجة الاستعلام عبارة عن POJO مع حقول @Relation . كل حقل هو طلب في حد ذاته ، وبالتالي فإن تشغيلها في معاملة واحدة يضمن نتائج متسقة بين الطلبات.

يتم @Delete @Insert و @Insert و @Insert ، والتي تحتوي على العديد من المعلمات ، تلقائيًا داخل المعاملة.


4. قراءة فقط ما تحتاجه


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


النظر في هذا الكائن User المعقدة:


 @Entity(tableName = "users") data class User(@PrimaryKey val id: String, val userName: String, val firstName: String, val lastName: String, val email: String, val dateOfBirth: Date, val registrationDate: Date) 

في بعض الشاشات ، لا نحتاج إلى عرض كل هذه المعلومات. وبالتالي ، بدلاً من ذلك ، يمكننا إنشاء كائن UserMinimal يحتوي فقط على البيانات الضرورية.


 data class UserMinimal(val userId: String, val firstName: String, val lastName: String) 

في فئة DAO ، نقوم بتعريف الاستعلام وتحديد الأعمدة الصحيحة من جدول users .


 @Dao interface UserDao { @Query(“SELECT userId, firstName, lastName FROM Users) fun getUsersMinimal(): List<UserMinimal> } 

5. السيطرة على التبعيات بين الكيانات ذات المفاتيح الخارجية


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


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


النظر في فئة User Pet . Pet لديه مالك - معرف المستخدم المشار إليه بواسطة المفتاح الخارجي.


 @Entity(tableName = "pets", foreignKeys = arrayOf( ForeignKey(entity = User::class, parentColumns = arrayOf("userId"), childColumns = arrayOf("owner")))) data class Pet(@PrimaryKey val petId: String, val name: String, val owner: String) 

إذا كنت ترغب في ذلك ، يمكنك تحديد الإجراء الذي يجب اتخاذه عند حذف الأصل أو تحديثه في قاعدة البيانات. يمكنك اختيار أحد الخيارات التالية: NO_ACTION أو RESTRICT أو SET_DEFAULT أو SET_DEFAULT أو CASCADE ، والتي تتصرف كما هو الحال في SQLite .


ملاحظة: في الغرفة ، يعمل SET_NULL مثل SET_NULL ، لأن لا تسمح لك الغرفة بعد بتعيين القيم الافتراضية للأعمدة.


6. قم بتبسيط استعلامات واحد لكثير باستخدامRelation


في مثال User السابق - حيوان Pet ، يمكننا القول أن هناك علاقة رأس بأطراف: يمكن للمستخدم امتلاك عدة حيوانات أليفة. لنفترض أننا نريد الحصول على قائمة المستخدمين مع حيواناتهم الأليفة: List<UserAndAllPets> .


 data class UserAndAllPets (val user: User, val pets: List<Pet> = ArrayList()) 

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


 @Query(“SELECT * FROM Users”) public List<User> getUsers(); @Query(“SELECT * FROM Pets where owner = :userId”) public List<Pet> getPetsForUser(String userId); 

ثم سنقوم بالتكرار على قائمة المستخدمين والوصول إلى جدول Pets كل مرة.


سيجعل التعليق التوضيحي Relation حياتنا أسهل: سيطلب تلقائيًا كائنات ذات صلة. لا يمكن تطبيق @Relation إلا على List أو Set . تحديث فئة UserAndAllPets:


 class UserAndAllPets { @Embedded var user: User? = null @Relation(parentColumn = “userId”, entityColumn = “owner”) var pets: List<Pet> = ArrayList() } 

في DAO ، نحدد استعلامًا واحدًا ، وسيقوم Room بالاستعلام عن جداول Users Pets وتعيين الكائنات من تلقاء نفسه.


 @Transaction @Query(“SELECT * FROM Users”) List<UserAndAllPets> getUsers(); 

7. تجنب الإخطارات الكاذبة لطلبات يمكن ملاحظتها


افترض أنك تريد الحصول على مستخدم بواسطة معرفه باستخدام طلب يمكن ملاحظته:


 @Query(“SELECT * FROM Users WHERE userId = :id) fun getUserById(id: String): LiveData<User> // or @Query(“SELECT * FROM Users WHERE userId = :id) fun getUserById(id: String): Flowable<User> 

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


إليك ما يحدث وراء الكواليس:


  1. يحتوي SQLite على مشغلات تقوم بإطلاق النار كلما UPDATE DELETE أو UPDATE أو INSERT في جدول.
  2. تنشئ الغرفة InvalidationTracker يستخدم Observers الذين يتتبعون جميع التغييرات في الجداول الملاحظة.
  3. LiveData كل من LiveData و LiveData الطلبات على InvalidationTracker.Observer # onInvalidated الإعلام . عند استلامها ، يحدث طلب متكرر.

تعرف الغرفة فقط أن الجدول قد تم تغييره ، لكن لا يعرف السبب وما الذي تغير. لذلك ، بعد طلب ثانٍ ، يتم إرسال نتيجة الطلب باستخدام LiveData أو LiveData . بسبب لا تخزن الغرفة أي بيانات في الذاكرة ؛ ولا يمكنها تحديد ما إذا كانت هي نفس البيانات أم لا.


يجب عليك التأكد من أن DAO يقوم بتصفية الطلبات ويستجيب فقط للكائنات الضرورية.


إذا تم تطبيق الاستعلام القابل للملاحظة باستخدام Flowables ، فاستخدم Flowable # diverUntilChanged .


 @Dao abstract class UserDao : BaseDao<User>() { /** * Get a user by id. * @return the user from the table with a specific id. */ @Query(“SELECT * FROM Users WHERE userid = :id”) protected abstract fun getUserById(id: String): Flowable<User> fun getDistinctUserById(id: String): Flowable<User> = getUserById(id) .distinctUntilChanged() } 

إذا قام الاستعلام بإرجاع LiveData ، فيمكنك استخدام MediatorLiveData ، والذي سيتلقى الكائنات المطلوبة فقط من المصدر.


 fun <T> LiveData<T>.getDistinct(): LiveData<T> { val distinctLiveData = MediatorLiveData<T>() distinctLiveData.addSource(this, object : Observer<T> { private var initialized = false private var lastObj: T? = null override fun onChanged(obj: T?) { if (!initialized) { initialized = true lastObj = obj distinctLiveData.postValue(lastObj) } else if ((obj == null && lastObj != null) || obj != lastObj) { lastObj = obj distinctLiveData.postValue(lastObj) } } }) return distinctLiveData } 

في DAOs ، الطريقة التي تُرجع LiveData هي طريقة public ، والطريقة التي تستعلم قاعدة البيانات protected .


 @Dao abstract class UserDao : BaseDao<User>() { @Query(“SELECT * FROM Users WHERE userid = :id”) protected abstract fun getUserById(id: String): LiveData<User> fun getDistinctUserById(id: String): LiveData<User> = getUserById(id).getDistinct() } 

انظر المثال رمز الكامل هنا .


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


انظر أيضا: 7 خطوات لاستخدام الغرفة. تجول لترحيل طلبك إلى Room

Source: https://habr.com/ru/post/ar442786/


All Articles