18/1/2014 - برمجة Android - إستخدام قواعد البيانات




سنتناول في هذا الدرس طريقة إستخدام قواعد البيانات في برامجنا التي نطورها لنظام آندرويد. سأركز في هذا الدرس إن شاء الله على الفئات والدوال الأساسية التي نحتاجها للتعامل مع قواعد البيانات، وبالتالي لن أبني مثالاً متكاملاً في هذا الدرس روماً للإختصار و البساطه، قد أطرح في درس آخر إن شاء الله مثالاً متكاملاً يستخدم قواعد البيانات وقوائم ListView التي تحدثت عنها مُسبقاً في عدد من الدروس إن شاء الله (هنا وهنا وهنا وهنا).

يأتي نظام آندرويد بشكل إفتراضي مع محرك قواعد البيانات SQLite وبالتالي يُمكنك إستخدامه في برنامجك لتخزين البيانات والتعامل معها من خلال لغة SQL. لإستخدام قواعد البيانات في برنامجنا سنتعامل بشكل أساسي مع فئتين وهما :SQLiteOpenHelper وSQLiteDatabase بالطبع مع مساعدة عدد من الفئات التي ستراها خلال هذا الدرس إن شاء الله.

كما أسلفت، بهدف إختصار الدرس وتبسيطه لن أبني هنا واجهة مُستخدم رسومية لطرح الأمثله، بل سأكتفي بإستخدام الداله i المُتوفره في الفئة Log، تقوم هذه الدالة بطباعة الرسائل التي تُمرر لها على إنها معلومات (الحرف i هنا إختصار لـ Information) وفي واجهة Eclipse ستُطبع هذه الرسائل في تبويب LogCat. يفترض هذا الدرس إنّك تعاملت مسبقاً مع قواعد البيانات ولك علم بمفاهيمها وكيفية إستخدام لغة SQL.

لنبدأ بإسم الله:

الفئة SQLiteOpenHelper
الخطوه الأولى في إستخدام قواعد البيانات في برامجنا هو إنشاء فئة خاصه بنا تَرث (Inheritance) الفئة SQLiteOpenHelper وهي فئة تُقدمها لنا بيئة تطوير آندرويد.

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

نرى إنّ إسم الفئه هنا يُعبّر عن وظيفتها، فهي فئة مُساعده لعملية فتح قاعدة بيانات مُعينه، لننتقل الآن إلى الجزء العملي ونكتب شيفرتنا :).

عند وراثة الكائن SQLiteOpenHelper يتوجب علينا إعادة كتابة (Override) دالتين أساسيتين، الأولى onCreate والتي سيتم إستدعاؤها في أوّل مرّه تُفتح قاعدة البيانات فيها، الثانية onUpgrade والتي سيتم إستدعاؤها عند تحديث قاعدة البيانات من نسخة قديمه إلى نسخة أحدث، وبالتالي يُمكنك كتابة الشيفره التي تُعدّل على قواعد البيانات وفقاً للإصدار الجديد في هذه الداله.

الجزئية الأكثر أهمية هي كتابة المُشيّد (Constructor) والذي بطبيعة الحال سيستدعي مُشيّد الفئة الأب ومن هُنا يُمكننا تحديد إسم قاعدة البيانات ونسختها.

لنبدأ بكتابة الشيفره:

public class Database extends SQLiteOpenHelper 
{


ببساطه أنشئنا هنا فئةً أطلقنا عليها إسم Database وورثنا الفئة SQLiteOpenHelper. ننتقل للتالي:

	private final static String DB_NAME = "DBTutorial";
private final static int DB_VERSION = 1;


عرّفنا هنا ثابتين سنستخدمهما فيما بعد إن شاء الله، الثابت الأول خزّنّا فيه إسم قاعدة البيانات التي نريد التعامل معها (وإنشاؤها في حال عدم وجودها)، أخترت إسم DBTutorial لقاعدة بياناتنا في هذا الدرس.

الثابت الثاني وهو DB_VERSION يحتوي على رقم إصدار قاعدة البيانات الحاليه وهو 1، لنفرض إننا قمنا فيما بعد بالتعديل على جداول قواعد البيانات وإضافة جداول جديده فسيتوجّب علينا تغيير رقم إصدار قاعدة البيانات إلى رقم أعلى وبالتالي يُمكن للنظام إستدعاء الدالة onUpgrade والتي يُفترض أن تُكتب فيها الشيفرة التي تقوم بالتعديلات على قواعد البيانات وفقاً للإصدار.

لنبدأ الآن ببناء مُشيّد فئتنا:

public Database( Context context ) 
{
super( context, DB_NAME, null, DB_VERSION );
}


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

SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)


البارامتر الأول واضح.
البارامتر الثاني هو إسم قاعدة البيانات.
البارامتر الثالث في هذا المثال سنُمرره على إنه null وبالتالي نُخبر النظام أن يستخدم الطريقه الإفتراضيه في الـ Cursor، وبشكل عام لا أعتقد إنّك ستحتاج لإستخدام هذا البارامتر حتّى في المشاريع الكبيره وبالتالي لن أتناول في هذا الدرس وظيفة هذا البارامتر، ولكن هذا لا يمنع أن تقرأ عنه ولو القليل فقد يفيدك :).
البارامتر الرابع وهو رقم إصدار قاعدة البيانات.

الأمور بسيط حتى الآن، فقد بنينا مُشيّد فئتنا الذي بدوره ينادي مُشيّد الفئة الأب وقمنا من خلاله بتحديد إسم قاعدة البيانات ورقم إصدارها، ننتقل للجزئية التاليه وهي كتابة الداله onCreate، كما أسلفنا فإنّ هذه الداله يتم مناداتها في أوّل مره يتم فتح قاعد البيانات (وهي DBTutorial في مثالنا الحالي)، وبالتالي الشيفره التي سنكتبها في هذه الداله هي شيفرة إنشاء جداول برنامجنا، إذا راجعنا الوثائق الرسمية سنجد إنّ تعريف الدالة onCreate كالتالي:

abstract void onCreate(SQLiteDatabase db)


مما يعني إنها تأخذ بارامتراً واحداً من نوع SQLiteDatabase ولا تعيد شيئاً، لنكتب شيفرتنا ولتبسيط الأمور لننشئ جدولاً واحداً إسمه names في هذا المثال:

@Override
public void onCreate( SQLiteDatabase db )
{
db.execSQL( "CREATE TABLE names( id integer primary key autoincrement, name text )" );
}


لاحظ هنا ظهرت فئه جديده علينا وهي SQLiteDatabase وقد إستخدمناها في كتابة شيفرتنا ولأنني سأتحدث في جزئيه منفصله عن هذه الفئه فلن أسبر أغوارها هنا، فكل ما عليك معرفته حالياً هو إنّه تُقدّم لنا داله إسمها execSQL تُساعدنا على تنفيذ جملة SQL، وكما تُلاحظ فجملتنا بسيطه هنا فكل ما قمنا به هو إنشاء جدول إسمه names يحتوي على حقل بإسم id وآخر بإسم name والمغزى من الحقلين واضح فالأول يُخزّن الرقم التعريفي والثاني يُخزّن الإسم كنص.

تبقّى علينا الآن كتابة الدالة onUpgrade، ولأنّ هذه النسخه هي الأولى من قاعدة بياناتنا مما يعني إنّه لاتوجد شيفره للكتابه مما يعني إننا سنترك هذه الداله فارغه كالتالي:

	@Override
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion )
{
}


لاحظ آخر بارامترين هنا فعندما يقوم النظام بإستدعاء هذه الداله فإنّه يُمرر رقم نسخة قاعدة البيانات السابقه ورقم نسخة قاعدة البيانات الجديده مما يُسهّل عليك في كتابة الشيفره عند تعدد إصدارات قاعدة البيانات.

الشيفره كامله:

package com.maastaar.dbtutorial;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class Database extends SQLiteOpenHelper
{
private final static String DB_NAME = "DBTutorial";
private final static int DB_VERSION = 1;

public Database( Context context )
{
super( context, DB_NAME, null, DB_VERSION );
}

@Override
public void onCreate( SQLiteDatabase db )
{
db.execSQL( "CREATE TABLE names( id integer primary key autoincrement, name text )" );
}

@Override
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion )
{
}
}


لننتقل الآن للفئه الأكثر أهميه وهي SQLiteDatabase.

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

قبل أن نتمكن من التعامل مع قاعدة البيانات وإضافة البيانات لها والتلاعب بهذه البيانات لابد لنا من فتح قاعدة البيانات عن طريق الفئة Database التي بنيناها في الشق الأول من هذا الدرس، يُمكنك وضع الشيفره التاليه ضمن شيفرة الـMain Activity:

Database dbHandler = new Database( getBaseContext() );


كُل ما قمنا به هنا هو إنشاء كائن من فئتنا Database وكما نعلم فإنّ فئتنا ترث الفئة SQLiteOpenHelper مما يعني إنّ جميع الدوال التي توفرها لنا الثانيه ستكون موجوده في الأولى، والدالة التي تهمنا حالياً هي getWritableDatabase والتي توفرها الفئة الموروثه SQLiteOpenHelper وكما يوحي لنا إسمها فإنّها تفتح (أو تُنشؤ) قاعدة البيانات لعمليات القراءة والكتابه، ووفقاً للوثائق الرسمية فإنّ تعريف هذه الداله كالتالي:

public SQLiteDatabase getWritableDatabase ()


حيث تُعيد لنا كائن من نوع SQLiteDatabase وهي الفئه التي نتحدث عنها حالياً والتي سنتمكن من خلالها بالتلاعب بالبيانات، هذا يعني إنّ خطوتنا التاليه في كتابة الشيفره هي فتح قاعدة البيانات كالتالي:

SQLiteDatabase db = dbHandler.getWritableDatabase();


يُمكننا الآن إستخدام الكائن db للتلاعب بالبيانات كما نشاء عن طريق الدوال التي تقدمها لنا الفئة SQLiteDatabase.

إضافة البيانات
سنستخدم لإضافة البيانات الدالة insert والتي توفرها لنا الفئة SQLiteDatabase وتعريفها كالتالي:

public long insert (String table, String nullColumnHack, ContentValues values)


تُعيد هذه الدالة الرقم التعريفي للصف (Row) الذي تم إضافته، في حال كانت القيمة -1 هذا يعني إنّ النظام فشل في إضافة الصف إلى قاعدة البيانات.

البارامتر الأول هو إسم الجدول الذي سنضيف له الصف.
البارامتر الثاني لا يهمنا حالياً وبالتالي لن أتحدث عنه.
البارامتر الثالث سيحتوي على البيانات التي نريد إضافتها.

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

في المثال التالي سنستخدم الدالة insert لإضافة صف واحد إلى الجدول names و ستكون قيمة الحقل name هي Android:

ContentValues info = new ContentValues();
info.put( "name", "Android" );

long id = db.insert( "names", null, info );

if ( id == -1 )
Log.i( "DBTutorial", "Insertion Failed" );
else
Log.i( "DBTutorial", "Newly Inserted Row's ID = " + id );


لنرى ما تقوم به هذه الشيفره جزءاً بعد جزء.

ContentValues info = new ContentValues();
info.put( "name", "Android" );


في هذه الأسطر قمنا بإنشاء كائن من الفئة ContentValues التي تحدثنا عنها منذ قليل، ووضعنا في هذه الحاوية البيانات التي نريد إضافتها إلى قاعدة البيانات حيث حددنا للحقل name القيمة Android حتى يتم إضافتها إلى قاعدة البيانات.

long id = db.insert( "names", null, info );


نقوم بعد ذلك بإستدعاء الدالة insert والتي ستقوم بإضافة الصف الجديد، نُمرر لها إسم الجدول وهو names كبارامتر أول، ونمرر لها حاوية البيانات التي أنشئناها منذ قليل كبارامتر ثالث ونُخزّن القيمة المُعاده في المتغير id.

if ( id == -1 )
Log.i( "DBTutorial", "Insertion Failed" );
else
Log.i( "DBTutorial", "Newly Inserted Row's ID = " + id );


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

مثال آخر يضيف عدد من الصفوف مُستخدماً مصفوفه نصيّه:

String listOfNames[] = { "Android", "iOS", "BlackberryOS", "Windows Phone" };

for ( String name: listOfNames )
{
ContentValues info = new ContentValues();
info.put( "name", name );

long id = db.insert( "names", null, info );

if ( id == -1 )
Log.i( "DBTutorial", "Inserting Failed for " + name );
else
Log.i( "DBTutorial", name + " Inserted and its ID = " + id );
}[code]

تحديث البيانات
لتحديث البيانات سنستخدم الدالة update ذات التعريف التالي:

[code]public int update (String table, ContentValues values, String whereClause, String[] whereArgs)


تُعيد هذه الدالة عدد الصفوف التي تأثرت بعملية التحديث (عدد الصفوف التي تم تحديثها).

البارامتر الأول إسم الجدول.
البارامتر الثاني حاويه للبيانات من نوع ContentValues تحتوي على البيانات الجديده المُراد وضعها في الصف بنفس الطريقه التي شرحناها في عملية إضافة البيانات.
البارامتران الثالث والرابع يخصّان جزئية where من جمل SQL، سنرى في مثالنا كيف نستخدمهما وطريقة إستخدامهما تنطبق على الدوال الأخرى بنفس الطريقة.

في المثال التالي سنُحدّث قيمة الحقل name إلى Android KitKat لجميع الصفوف التي قيمة name فيها هي Android، ولو أردنا قول هذه الجملة بلغة SQL فستكون كالتالي:

UPDATE names SET name='Android KitKat' WHERE name='Android'


لنرى كيف ننفذها مع الدالة update:

ContentValues info = new ContentValues();
info.put( "name", "Android KitKat" );

long updatedRows = db.update( "names", info, "name=?", new String[] { "Android" } );

Log.i( "DBTutorial", "Number of updated rows = " + updatedRows );


أنشأنا أولاً الحاوية مع القيمة الجديده من خلال أول سطرين ولا شيء جديد علينا هنا، ثم أستدعينا الداله update في السطر الرابع:

long updatedRows = db.update( "names", info, "name=?", new String[] { "Android" } );


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

وفي حال وجود أكثر من شرط يمكننا إستخدام الكلمات المفتاحيه AND او OR الخاصه بلغة SQL بشكل عادي ضمن البارامتر الرابع، ثم وضع قيم علامات الإستفهام في البارامتر الخامس بالترتيب.

و أخيراً يتم طباعة القيمه المُعاده من الدالة update والتي كما أسلفنا عدد الصفوف المُتأثره في عملية التحديث هذه.

حذف البيانات
لحذف البيانات نستخدم الداله delete ذات التعريف التالي:

public int delete (String table, String whereClause, String[] whereArgs)


تُعيد هذه الداله عدد الصفوف المتأثره في عملية الحذف (أو بشرح آخر، عدد الصفوف التي تم حذفها في هذه العملية).

البارامتر الأول إسم الجدول.
البارامترات الثاني والثالث يخصان جزئية where من جملة SQL ويتم إستخدامها بنفس الطريقة التي شرحناها منذ قليل في "تحديث البيانات".

المثال التالي يحذف جميع الصفوف من الجدول names:

long deletedRows = db.delete( "names", null, null );

Log.i( "DBTutorial", "Number of deleted rows = " + deletedRows );


إحضار البيانات
وأخيراً لإحضار البيانات يمكننا إستخدام الدالة query، إذا راجعت الوثائق الرسميه فإنك ستجد أكثر من تعريف لهذه الداله يُمكنك إستخدام ما يناسبك منها وفقاً للإستعلام الذي تبنيه، لمثالنا سنستخدم التعريف التالي:

public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)


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

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

لنبني مثالاً يُحضر جميع صفوف الجدول names وترتيب الأسماء أبجدياً، نستدعي أولاً الداله query بطريقة تناسب إستعلامنا:

Cursor namesCur = db.query( "names", null, null, null, null, null, "name ASC" );


لاحظ إننا مررنا null لأغلبية البارامترات لأننا لا نحتاجها في إستعلامنا، كُل ما احتجناه هُنا جزئية الترتيب من جملة SQL وبالطبع لابد من تمرير إسم الجدول.

يُفترض من الكائن namesCur الآن أن يكون مؤشراً على صفوف البيانات المُعاده من الإستعلام، هذا المؤشر يبدأ من الموقع (Position) رقم -1، وأول صف حقيقي من البيانات التي تم إحضارها يُمثَّل بالموقع رقم 0، وبالتالي لإحضار بيانات أول صف يجب علينا الإنتقال من الموقع -1 إلى الموقع 0، نستخدم الداله moveToNext التي تُقدمها الفئة Cursor لأداء هذه الوظيفه، وستقوم هذه الداله بالإنتقال إلى الصف التالي عند إستدعاءها في كل مره وتُعيد القيمه true، وعندما لا يكون هناك صف تالي (بمعنى إننا حالياً في آخر صف تم إحضاره من قاعده البيانات) فإنها ستعيد القيمة false، وبالتالي من السهل علينا كتابة حلقة تكرار تقرأ جميع الصفوف التي تم إحضارها من قاعدة البيانات كالتالي:

while ( namesCur.moveToNext() )
{


ما نريده الآن هو قراءة قيم الحقول في الصف الذي يؤشر عليه مؤشرنا (cursor) حالياً، ويتم ذلك عن طريق مجموعه من الدوال التي تقدمها لنا الفئة Cursor تبدأ أسماؤها بـ get وتنتهي بنوع الحقل، فمثلاً الحقل id في جدول names من نوع Integer وبالتالي لأخذ قيمته يجب علينا إستخدام داله بإسم getInt، وكذا بالنسبه لحقل name فهو من نوع String (أو varchar بلغة قواعد البيانات) وبالتالي يجب عليها إستخدام داله بإسم getString.

الدوال من نوع get والتي تقدمها الفئة Cursor كما أسلفنا تأخذ بارامتراً واحداً وهو رقم الحقل المُراد أخذ بياناته حيث تبدأ الأرقام من 0، وبالتالي رقم الحقل id في جدولنا هو 0، ورقم الحقل name في جدولنا هو 1، ولكن هناك داله مُساعده تقدمها لنا نفس الفئة Cursor وهي getColumnIndex فكل ما علينا هو أن نمرر لها إسم الحقل وهي ستُعيد لنا رقم الحقل المطلوب بدون أن نبذل جهداً في حفظ أرقام الحقول (أو قد يحصل ما هو أسوأ وهو تغيّر أرقام الحقول وفقاً لتغييرات في الجداول في نسخه جديده من البرنامج مما سيجعلنا نضطر أن نعدّل على شيفرتنا القديمه).

نستكمل كتابة الشيفره حيث نأخذ الرقم التعريفي للصف الحالي وقيمة الحقل name ونقوم بطباعتها كالتالي:

int id = namesCur.getInt( namesCur.getColumnIndex( "id" ) );
String name = namesCur.getString( namesCur.getColumnIndex( "name" ) );

Log.i( "DBTutorial", id + ":" + name );
}


لاحظ إستخدمنا الدالتين getInt و getString لأخذ القيم و إستخدمنا داخلهما الداله getColumnIndex لأخذ رقم الحقل عن طريق تمرير إسمه.

الشيفره كامله:

Cursor namesCur = db.query( "names", null, null, null, null, null, "name ASC" );

while ( namesCur.moveToNext() )
{
int id = namesCur.getInt( namesCur.getColumnIndex( "id" ) );
String name = namesCur.getString( namesCur.getColumnIndex( "name" ) );

Log.i( "DBTutorial", id + ":" + name );
}


مثال آخر لأخذ صف واحد من قاعدة البيانات عن طريق تحديد رقمه التعريفي (id) ولا جديد علينا في هذا المثال إلا الدالة getCount:

Cursor nameCur = db.query( "names", null, "id=?", new String[] { String.valueOf( 1 ) }, null, null, null );

if ( nameCur.getCount() <= 0 )
Log.i( "DBTutorial", "No Such Row" );
else
{
nameCur.moveToNext();

Log.i( "DBTutorial", nameCur.getString( nameCur.getColumnIndex( "name" ) ) );
}


كما هو واضح فإنّ الداله getCount تُعيد عدد الصفوف التي تم إعادتها من قاعدة البيانات وفقاً لإستعلامنا، وبالتالي يُمكننا إستخدامها للتحقق إن كان فعلاً هناك صف متوافق مع متطلبات إستعلامنا أم لا.

ولاحظ إستخدامنا لـ moveToNext لنفس السبب الذي ذكرناه في الأعلى وهو بدء المؤشر من الموقع -1 وفي الحقيقه البيانات تبدأ من المؤشر 0، يمكننا إعادة صياغة هذه الشيفره كالتالي:

		Cursor nameCur = db.query( "names", null, "id=?", new String[] { String.valueOf( 1 ) }, null, null, null );

if ( nameCur.moveToNext() )
Log.i( "DBTutorial", nameCur.getString( nameCur.getColumnIndex( "name" ) ) );
else
Log.i( "DBTutorial", "No Such Row" );


والفرق يكمن في إستخدامنا للداله moveToNext في التحقق من وجود الصف المنشود بدلاً من إستخدام الداله getCount.

هذا كُل شيء :)، إن كان هناك أي سؤال في محتوى الدرس يُمكنك مراسلتي عن طريق البريد الإلكتروني أو على حساب Twitter وسأسعى جاهداً إن شاء الله لأن أجيب على سؤالك.

والحمدلله رب العالمين.