التعامل مع SQLite في Android

بسم الله الرحمن الرحيم

كثير من التطبيقات تحتاج إلى تخزين في البيانات اندرويد, كمذكرة, أو أسماء وأرقام أو تواريخ وغيرها, في اندرويد يوجد عدد من الخيارات لتخزين البيانات

كـ SharedPreferences لكنه كما هو مكتوب في Documentation .

If you have a relatively small collection of key-values that you’d like to save, you should use the SharedPreferences APIs.

إذا فهو مخصص لبيانات بسيطة, كحفظ اللغة التي اختارها المستخدم في التطبيق, لكن إذا كان حجم البيانات كبير, كحفظ عدد من المذكرات, أو تسجيل طلاب -وهو المثال الذي سوف نستعرضه-, سنحتاج لتعامل مع قواعد البيانات -Database-,  في اندرويد نستخدم SQLite كقاعدة بيانات وفيها عدد من المميزات, فهي صغيرة الحجم, ولا تحتاج لأي إعدادت لاستخدامها, سريعة – فعلى سبيل المثال عند إدخال 25000 عنصر ستحتاج sqlite إلى 0.7, MySQL إلى 2.2,  PostgreSQL إلى 4.9 ثانية.

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

  • تعريف Schema and Contract
  • بناء قاعدة البيانات باستخدام SQLiteOpenHelper
  • تطبيق عمليات  CRUD على قاعدة البيانات

سأبدأ بخطوة, وهي تعريف class student بعد ذلك سنمر على هذه الخطوات, سيكون شكل  class student كالتالي:

class بسيط جدا فيه ثلاث متغيرات, اسم الطالب, والمادة والمُعّرف.

تعريف Schema and Contract

بداية Schema نعني بها هيكل قاعدة البيانات, أو الشكل الخاص بها, ونعرفها داخل class نسميه contract بالطريقة التالية

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

  • بداية تم تعريف private constructor , ببساطة لأنه  ليس الغرض من هذا class أنتنشئ منه object وبهذا الأسلوب من التعريف أمنعك من أن تنشئ object.
  •   inner class الغاية منه أن تعرف فيه اسم الجدول, وجميع الحقول الخاصه به, كم فعلنا مع Student Table, لماذا؟ أولا, يمكنك معرفة شكل الجدول بمجرد إلقاء نظرة على هذا class, ثانيا, تستخدم جميع المتغيرات التي عرفتها داخل هذا class في أي class أخر داخل التطبيق, وهذا يقلل من حدوث الخطأ, بالإضافة إلى أنه يسهل التعديل.
  • في حال أردنا إضافة جدول أخر, سنقوم بعمل  inner class خاص به, لنفترض أننا نريد تعريف جدول لـ course سيكون شكله كالتالي
  • BaseColumns عبارة عن interface يعطيك id لكل عملية إدخال تقوم بها, بالإضافة إلى عدد rows لديك في الجدول, شكل interface كالتالي

    قد لا يكون واضح الغاية والغرض منه لكن ثق تماما أنه بسيط وسيتضح لاحقا.
  • في حال أردنا تعريف أي متغير أو ثابت مشترك لجميع Tables فإنه يكون في class الـ contract, في حال أردنا تعريف أي متغير أو ثابت خاص بـ Table معين, فإنه يكون داخل inner class الخاص به.

بناء قاعدة البيانات باستخدام SQLiteOpenHelper

الان سوف ننتقل إلى بناء database الخاصة بنا, سنقوم في البداية بعمل extends لـ SQLiteOpenHelper وهو class سيساعدنا في بناء database, سيطلب منها عمل implement لثلاث دوال وهي

  1. onCreate(), والتي نستخدمها لإنشاء قاعدة البيانات الخاصة بنا.
  2. onUpgrade(), والتي نستخدمها في حال قمنا بإجراء أي تعديل على Schema الخاصة بالجدول.
  3. constructor يطابق super, سنمر عليه حالاً.

ليكون شكل class كالتالي

الان سنقوم بعمل implementation لهم دالة دالة, ونحدث الكود لنبدأ بالـ constructor, نلاحظ أنه يطلب اسم database – وليس اسم table -, بالإضافة إلى رقم النسخة الخاصة بـ database – تبدأ غالبا من 1, ومع كل تحديث تجريه على Schema الخاصة بالجداول تقوم بزيادة رقم الإصدار وهكذا-, و context و factory, سنقوم بتعريف متغيرات static لاسم واصدار Database ونجعلfactory بقيمة null, والـ context نحصل عليه عن طريق constructor

لان سننتقل لـ onCreate هذه الـmethod تستدعى لمرة واحدة في أول إنشاء لـ Database , وسننفذ فيها أمر واحد فقط وهو إنشاء database, وسنقوم بعمل  SQL query عادية جدا, ثم ننفذها داخل عن طريق  db

أما onUpgrade فيتم استدعاؤها إذا تغير رقم VERSION في التطبيق, وببساطة فهي تقوم بتحديث شكل الجدول عن طريق حذف الجدول السابق وإعادة إنشاء الجدول

وبهذا  يكون اكتمل لدينا class StudentHelper.

تطبيق عمليات  CRUD على قاعدة البيانات

CRUD هو اختصار لعمليات الـDatabase الرئيسية, الإنشاء CREATE, القراءة READ, التحديث UPDATE, الحذف DELETE, بعد أن قمنا بتعريف وبناء شكل قاعدة البيانات الخاصة بنا سنبدأ هنا في التعامل معها داخل الـ MainActivity

نبدأ بـ CREATE والتي يقابلها INSERT في SQL, سنقوم بإضافة student لـ Database

في البداية قمنا بإنشاء object من نوع StudentHelper, بعد ذلك سننشئ object يستطيع القراءة والكتابة في database باستدعاء هذه الدالة getWritableDatabase, بعد ذلك نستخدم ContentValues لننشئ row  جديد, ContentValues بسيط جدا فهو يستخدم key/value, الـ Key دائما ما يكون أسم العمود, و value هو القيمة المراد تخزينها.

ثم قمنا باستدعاء دالة insert التي تطلب منا اسم الجدول, و nullColumnHack سنجعل قيمته null, أخيرا ContentValues الذي قمنا بتجهيزه سلفا, ستعيد لنا دالة insert إما رقم row, أو -1 عند الخطا.

وبهذا نكون أتممنا أول عملية في CRUD

الان سنقوم بعملية READ ويقابلها SELECT في SQL, إذا سنقوم باسترجاع البيانات الموجودة داخل Database, سأزيل كود عملية Insert ليكون الكود أكثر وضوحا

لدينا هنا عدة أمور لنوضحها

  1. قمنا بإنشاء object عن طريق استدعاء getWritableDatabase لنتمكن من قراءة البيانات.
  2. projection ببساطة هي Array of String, نقوم من خلالها بإختيار الحقول التي نريد استرجاعها, في مثالنا قمنا بإرجاع الإسم والمادة بالإضافة إلىid, لكن لو أردنا فقط  id فسنقوم بالتالي

    وبهذا سيرجع لنا فقط id.
  3. selection هي string نقوم فيه بتحديد الشرط الذي نريد أسترجاع الـrow بناء عليه, ففي المثال نقول إذا كان name يساوي  Ali رجع row
  4. selectionArgs هي Array of String نضع فيه القيم التي نريد التحقق منها كما في الكود السابق فهي مكملة لـ selection, selection تقوم بوضع الشرط, و selectionArgs تحتوي القيم.
  5. sortOrder  هي جملة string الغاية منها ترتيب البيانات الراجعة من الجدول تنازلي -DESC-, أو تصاعدي -ASC-
  6. cursor هو عبارة عن مؤشر, يؤشر على البيانات الراجعة لنا, لتقريب الصورة, تخيل أن البيانات عبارة عن ملف نصي, والـ cursor عبارة عن ذاك المؤشر الذي يؤشر في بداية الملف.
  7. قمنا باستدعاء دالة query وأعطيناها جميع المعلومات التي تحتاج لتعطينا البيانات التي نحتاج, اسم الجدول, والشرط, والقيم الخاصة بالشرط, والترتيب الخاص بالبيانات.
  8. أخيرا قمنا بالتحقق إذا ما كان cursor يقف على بيانات أم لا, cursor.moveToFirst سترجع لنا قيمة false في حال لم يكن هناك أي بيانات, في حال كانت القيمة true, نقوم بأخذ row الأول وننتقل لثاني عن طريق cursor.moveToNext والتي في حال كنا في أخر row  ستعيد لنا قيمة false ونخرج من loop.
    لقراءة البيانات من cursor استدعينا دالة getString والتي تحتاج int, فحصلنا عليها عن طريق دالة getColumnIndex والتي تعطيها أسم الحقل وترجع لك رقمه, لتوضيح أكثر لدينا ثلاث حقول _id, و name و course فيكون رقم index 0 للأول, 1 لثاني, 2 لثالث. وهكذا.

ننتقل الان للأمر الثالث من CRUD, وهو update, ويقابله  UPDATE في SQL, الأمر في غاية البساطة, سنقوم بإضافة البيانات التي نرغب في تعديلها في ContentValues ثم نبحث عن row ونقوم بالتعديل عليه.

لا يوجد العديد من الأشياء الجديدة فقط نلاحظ استخدام دالة update, والتي ترجع لنا قيمة rows التي تم تعديلها.

أخيرا أمر DELETE, وقد يكون الأبسط, خصوصا بعد أن تعرفنا على بقية العمليات, ستحتاج إلى selection تعرف فيه query, ثم selectionArgs تعطيه القيم التي تحذف الـrow بناء عليها, وأخيرا تنفذ العملية بدالة delete التي تطلب من أسم الجدول و selection, و selectionArgs

 

نقطة أخيرة يجب التنبيه عليها, دالتي getReadableDatabase و  getWritableDatabase مكلفة جدا في الاستدعاء لذا مادمت تعتقد أنك ستحتاج إلى التعامل مع الـ Database , لا تستدعي دالة close, وجميل جدا أن تضع دالة close داخل onDestroy.

 

 

إلى هنا أكون قد وصلت إلى نهاية هذه المقالة, أتمنى أن أكون وفقت في تبسيط المعلومة, في حال كان هناك أي ملاحظات أو تساؤل يمكنك التواصل معي على حسابي في تويتر

والسلام عليكم ورحمة الله وبركاته.

 

المصادر

اترك تعليقاً