ch23-part-1-Android-Database-sqlite-cover

آموزش اندروید-فصل ۲۴: پایگاه داده در اندروید (قسمت دوم)

در مطلب قبلی مقدمات کار با دیتابیس یا پایگاه داده را گفتم. در این مطلب می‌خواهم نحوه عمل سه تابع باقیمانده یعنی update و delete و read را که از چهار عمل اصلی دیتابیس‌اند توضیح دهم. همان پروژه مطلب قبلی را ادامه خواهم داد.

۱- تابع delete:

همیشه قرار نیست فقط اطلاعات را در دیتابیس ذخیره کنیم. گاهی لازم است برخی اطلاعات را از پایگاه داده حذف کنیم. برای حذف کردن اطلاعات باید به دیتابیس بگوییم که دقیقاً چه رکوردهایی را حذف کند. اگر فراموش کنیم شرط را بگوییم، دیتابیس همه اطلاعات آن جدول را حذف خواهد کرد! این تابع ممکن است یک یا چند رکورد مطابق با شرطی که می‌گوییم پیدا کند و آن‌ها را حذف کند و ممکن است هیچ رکوردی مطابق شرط ذکر شده نداشته باشیم. این تابع همواره عددی را برمی‌گرداند که تعداد رکوردهای حذف شده است. برای شروع فرض کنید می‌خواهیم فردی با یک شناسه (ID) خاص را که به عنوان پارامتر به این تابع داده‌ایم حذف کنیم. برای این کار ابتدا باید شرط را تعریف کنیم:

public int deletePerson(long id) {
    String whereClause = "id=?";
    // ...
}

whereClause یعنی عبارت شرط. علت این نامگذاری این است که از همین نام در مستندان اندروید استفاده شده است. با تعریف عبارت شرطی به این شکل می‌گوییم رکوردهایی که id آن‌ها را اعلام خواهیم کرد. این علامت سوال را باید در پارامتر بعدی مقداردهی کنیم:

public int deletePerson(long id) {
    String whereClause = "id=?";
    String[] whereArgs = new String[] {String.valueOf(id)};
    // ...
}

در خط دوم یک آرایه از String یا رشته تعریف کردیم. در این آرایه، به ازای هر یک علامت سوال که در whereClause تعریف کردیم باید یک مقدار قرار دهیم. ترتیب آن‌ها هم مهم است. id را به String تبدیل کردیم و در این آرایه قرار دادیم. به عکس زیر دقت کنید:

ch24-01-android-sqlite-where-clause-args

برای وضوح در عکس از دو پارامتر استفاده کردم تا علت استفاده از آرایه را ببینید. حالا وقت آن است که واقعاً یک رکورد را از پایگاه داده حذف کنیم:

public int deletePerson(long id) {
    int noOfDeletedRecords = 0;
    String whereClause = "id=?";
    String[] whereArgs = new String[] {String.valueOf(id)};

    SQLiteDatabase database = null;

    try {
        database = sqLiteOpenHelper.getWritableDatabase();
        noOfDeletedRecords = database.delete("tbl_persons", whereClause, whereArgs);
    } catch (Exception ex) {
        Log.d("Database", "Exception:" + ex.getMessage());
    } finally {
        if( database != null && database.isOpen()) {
            database.close();
        }
    }

    return noOfDeletedRecords;
}

به همین سادگی صفر یا یک رکورد را حذف می‌کنیم! چرا صفر؟ چون ممکن است هیچ رکوردی با id ارسالی وجود نداشته باشد. و چرا نه بیشتر از یک رکورد؟ چون id شناسه است و تکراری ندارد. یعنی یا یک رکورد با این شناسه وجود ندارد (صفر) یا فقط و فقط یکی وجود دارد.

در دنیای واقعی شرط‌ها معمولاً خیلی پیچیده‌تر از این‌ها است ولی من فقط می‌خواهم کار با دیتابیس در اندروید را آموزش دهم و خود دیتابیس مباحث خیلی پیشرفته‌تری دارد که برای آن‌ها کتاب‌ها نوشته شده است و حتا در دانشگاه در یک درس مستقل تدریس می‌شود.

۲- تابع update:

گاهی لازم است مقداری را در دیتابیس تصحیح کنیم. فرض کنید می خواهیم نام شخصی که id یا شناسه‌ش ۱ است به Ehsan تغییر دهیم. برای این کار به سراغ تابع update می‌رویم. تابع update چیزی است بین save و delete. از آنجایی که باید دقیقاً مشخص کنیم چه رکورد یا رکوردهایی باید اصلاح شوند شبیه delete است و فرایند تعیین ستون‌هایی که قرار است مقدار آن‌ها تغییر یابد شبیه save است.

اول شرط را تعیین می‌کنیم:

public int updatePerson(Person person) {
    String whereClause = "id=?";
    String[] whereArgs = new String[] {String.valueOf(person.getId())};
    //...
}

حالا باید مقادیر جدید این رکورد (یا رکوردها) را مشخص کنیم. می‌توانیم مقداری را که می‌خواهیم در قالب یه شی Person به این تابع بدهیم و با استفاده از ContentValues ستون‌ها و مقادیر جدید آن‌ها را به دیتابیس اعلام کنیم:

public int updatePerson(Person person) {
    String whereClause = "id=?";
    String[] whereArgs = new String[] {String.valueOf(person.getId())};

    ContentValues values = new ContentValues();
    values.put("name", person.getName());
    values.put("family", person.getFamily());
    //...
}

تابع update هم مثل delete یک عدد برمی‌گرداند که تعداد رکوردهایی است که اصلاح شده‌اند. مقدار آن همی می‌تواند صفر یا یک یا هر عدد بزرگ‌تری باشد. شکل کامل این تابع این است:

public int updatePerson(Person person) {
    int noOfUpdatedRecords = 0;
    String whereClause = "id=?";
    String[] whereArgs = new String[] {String.valueOf(person.getId())};

    SQLiteDatabase database = null;
    
    try {
        ContentValues values = new ContentValues();
        values.put("name", person.getName());
        values.put("family", person.getFamily());

        database = sqLiteOpenHelper.getWritableDatabase();
        noOfUpdatedRecords = database.update("tbl_persons", values, whereClause, whereArgs);
    } catch (Exception ex) {
        Log.d("Database", "Exception:" + ex.getMessage());
    } finally {
        if( database != null && database.isOpen()) {
            database.close();
        }
    }

    return noOfUpdatedRecords;
}

۳- تابع read:

حالا وقت آن است که آخرین قطعه این پازل را کامل کنیم. چطور باید اطلاعات را از پایگاه داده استخراج کنیم؟ یا چطور دیتابیس را بخوانیم؟ ابتدا باید شرط را مشخص کنیم: کدام رکورد یا رکوردها را می‌خواهیم بخوانیم؟ و چطور می‌خواهیم آن‌ها را مرتب کنیم و …

public Person readPerson(long id) {
    String[] columns = new String[] {"*"};
    String selection = "id=?";
    String[] selectionArgs = new String[] {String.valueOf(id)};
    String groupBy = null;
    String having = null;
    String orderBy = null;
    String limit = null;
}

حالا با استفاده از تابع query از کلاس SQLiteDatabase از دیتابیس می‌خواهیم از جدول tbl_persons یک رکورد که id آن به تابع فرستاده شده است را استخراج کرده و آن را به شی‌ای از نوع Person تبدیل کند و برای ما بفرستد. اگر چنین رکوردی در دیتابیس نباشد، این تابع null برمی‌گرداند. اطلاعاتی که دیتابیس برای ما می‌فرستد در قالب یک شی از کلاس Cursor است. در ادامه با این کلاس آشنا خواهید شد:

public Person readPerson(long id) {
    Person person = null;
    String[] columns = new String[] {"*"};
    String selection = "id=?";
    String[] selectionArgs = new String[] {String.valueOf(id)};
    String groupBy = null;
    String having = null;
    String orderBy = null;
    String limit = null;

    SQLiteDatabase database = null;
    try {
        database = sqLiteOpenHelper.getWritableDatabase();
        Cursor cursor = database.query("tbl_persons", columns, selection, selectionArgs, groupBy, having, orderBy, limit);
        // ...
    } catch (Exception ex) {
        Log.d("Database", "Exception:" + ex.getMessage());
    } finally {
        if( database != null && database.isOpen()) {
            database.close();
        }
    }

    return person;
}

قبل از این که به سراغ Cursor و تبدیل آن به یک شی Person برویم لازم است چند چیز را توضیح دهم:

۱- تابع query چهار شکل متفاوت دارد. ما اینجا از ساده‌ترین آن‌ها استفاده کردیم!

۲- مقدار ستاره که در تعریف columns استفاده کردیم یعنی همه ستون‌ها. اگر بخواهیم یک یا چند ستون خاص را انتخاب کنیم باید نام تک تک آن‌ها را در این آرایه بیاوریم:

String[] columns = new String[]{"id", "name", "family"};

۳- selection و selectionArgs مشابه همان‌ها هستند که در توابع delete و update دیدید. اینجا در پیروی از قواعد نامگذاری اندروید، به این نام‌های جدید تغییر کرده‌اند!

۴- چهار مقدار دیگر که مقدار آن‌ها را null گذاشتیم، شیوه سازماندهی اطلاعات دریافتی را مشخص می‌کنند. با این که این موارد بسیار کاربردی هستند در حال حاضر نیازی به آن‌ها نداریم. شاید در آینده از این‌ها هم استفاده کردیم!

بالاخره رسیدیدم به قسمت جالب ماجرا! تبدیل اطلاعات دریافتی از دیتابیس که در قالب Cursor هستند و تبدیل آن‌ها به یک شی ار نوع Person.

اولین کاری که باید بکنیم این است که چک کنیم آیا Cursor دارای مقدار است یا این که خالی است؟ اما قبل از آن لازم است کمی درباره ساختار Cursor توضیح بدهم. شکل زیر را ببینید:

ch24-02-android-sqlite-cursor

این تصویر بسیار ساده اصول ساختاری Cursor را نشان می‌دهد. نشان‌گری که در سمت چپ عکس می‌بینید در طول Cursor حرکت کرده و هر بار یک رکورد را می‌خواند. در ابتدای کار این نشانگر اصطلاحاً قبل از اولین رکورد است. اگر ما بتوانیم این نشانگر را به اولین رکورد منتقل کنیم معنی‌اش این است که حداقل یک رکورد در این Cursor وجود دارد. بنابراین کد زیر را می‌نویسیم:

Cursor cursor = database.query("tbl_persons", columns, selection, selectionArgs, groupBy, having, orderBy, limit);
if(cursor != null && cursor.moveToFirst()) {
    // ...
}

اگر تابع moveToFirst مقدار false برگرداند یعنی این که نشانگر فوق نتوانسته است اولین رکورد را بخواند. معنی دیگرش این است که چنین رکوردی وجود ندارد!

اگر تابع moveToFirst مقدار true برگرداند یعنی ما حداقل یک رکورد داریم و می‌توانیم آن را بخوانیم. باز به عکس بالا نگاه کنید. هر رکورد در Cursor شامل فیلدهای اطلاعاتی است. در Cursor این فیلدها نام ندارند و ترتیب آن‌ها مهم است. شمارش آن‌ها از صفر شروع می‌شود:

if(cursor != null && cursor.moveToFirst()) {
    int idIndex = 0;
    int nameIndex = 1;
    int familyIndex = 2;
    // ...
}

حالا تک تک این فیلدها را می‌خوانیم و مقدار آن‌ها را در متغیرهای جاوا ذخیره می‌کنیم:

long personId = cursor.getLong(idIndex);
String personName = cursor.getString(nameIndex);
String personFamily = cursor.getString(familyIndex);

در خط اول می‌گوییم فیلدی که index آن صفر است (اولین فیلد) را به عنوان یک مقدار long بخوان و مقدار آن را در متغیزی به نام personId ذخیره کن. خطوط بعدی هم به همین ترتیب!

الان همه چیزهای مورد نیاز برای ساخت یک شی Person را داریم:

person = new Person();
person.setId(personId);
person.setName(personName);
person.setFamily(personFamily);

شکل کامل تابع readPerson این است:

public Person readPerson(long id) {
    Person person = null;
    String[] columns = new String[]{"id", "name", "family"};
    String selection = "id=?";
    String[] selectionArgs = new String[]{String.valueOf(id)};
    String groupBy = null;
    String having = null;
    String orderBy = null;
    String limit = null;

    SQLiteDatabase database = null;
    try {
        database = sqLiteOpenHelper.getWritableDatabase();
        Cursor cursor = database.query("tbl_persons", columns, selection, selectionArgs, groupBy, having, orderBy, limit);
        if(cursor != null && cursor.moveToFirst()) {
            int idIndex = 0;
            int nameIndex = 1;
            int familyIndex = 2;

            long personId = cursor.getLong(idIndex);
            String personName = cursor.getString(nameIndex);
            String personFamily = cursor.getString(familyIndex);

            person = new Person();
            person.setId(personId);
            person.setName(personName);
            person.setFamily(personFamily);
        }

    } catch (Exception ex) {
        Log.d("Database", "Exception:" + ex.getMessage());
    } finally {
        if (database != null && database.isOpen()) {
            database.close();
        }
    }

    return person;
}

پووفففف! بالاخره تموم شد! البته فقط چهر عمل اصلی و آن هم با ساده‌ترین شکل ممکن پیاده‌سازی!

در مطلب بعدی این مثال را با ListView کامل می‌کنیم. لیست شیوه نمایش تعداد زیادی اطلاعات هم نوع است. مثلا همه اعضای یک خانواده یا کلاس یا …

اسمارت‌لب را دنبال کنید و به دوستان خودتان هم معرفی کنید! کانال اسمارت‌لب در تلگرام: http://telegram.me/smartlabir

facebooktwittergoogle_plusredditpinterestlinkedinmailfacebooktwittergoogle_plusredditpinterestlinkedinmail




18 فکر می‌کنند “آموزش اندروید-فصل ۲۴: پایگاه داده در اندروید (قسمت دوم)

  1. رضا

    سلام. واقعا عالیه کارتون… خیلی کامل،نفید و با دقت و سلیقه…
    جوری توضیح دادین که حتی اگه برنامه نویسی هم بلد نباشیم باز متوحه بشیم منظورتون چیه…
    فقط یه خواهش دارم، من مبحث دیتا بیس رو خوب نمیتونم درک کنم. میشه یه آموزش هم از دیتابیس برای ساخت یه کتاب ۱۰ صفحه بذارید تا ببینم دقیقا برای ساخت یه کتاب، یا هر اپلیکشن تو این شرایط،دقیقا دیتابیس جه استفاده ای میشه ازش؟ اصلا دیتابیس دقیقاچه نقشی تو ساخت یه کتاب داره و دقیقا کجای کار لازم میشه وچجوری؟!
    خیلی خوب میشد اگه یه پست هم دراین باره بذارید.
    درواقع من میخام دیتابیس رو با یه مثال کاربردی مثل ساخت یه کتاب کوچک به صورت گام به گام یاد بگیرم.

    پاسخ
  2. محسن موسی زاده

    در هنگام import کردن AppCompatActivity شناسایی آن توسط اکلیپس با خطا مواجه می گردد. لطفا راهنمایی فرمایید.
    import android.support.v7.app.AppCompatActivity;

    پاسخ
  3. Yousef

    سلام، پستتون در مورد کار با دیتابیس در اندروید رو خوندم اینقد عالی بود گفتم حیفه ازتون تشکر نکنم…
    مطالبتون خیلی خوب و کاربردیند.
    براتون آرزوی بهترین ها رو دارم!

    پاسخ
  4. رضا

    با سلام و خسته نباشید، ممنون از پست واقعا خوبتون.
    من یه نکته رو متوجه نشدم، این پارامتر کوئری String selection = “id=?”; که فرمودین اگر id استفاده بشه یعنی اینکه کوئری بر اساس id هست.
    حتی پست بعدیتون رو هم خوندم ولی متاسفانه باز هم متوجه نشدم.

    پاسخ
    1. علی بهزادیان نژاد نویسنده

      سلام
      تو این قسمت خوندن Person بر اساس id است. id هم به عنوان ورودی به تابع داده شده است. دقیقا کجاش رو متوجه نشدید؟

      پاسخ
      1. رضا

        سلام
        ممنون از پاسختون، نکته ای که من متوجه نشدم اینه که وقتی id رو برای selection در تابع read انتخاب می کنیم آیا چیزی رو برمی گردونه که ما وقتی اپمون کامل شد بتونیم ببینیم ( مثلا مثل انتخاب ستون ها در پارامتر دوم) یا نه؟

        پاسخ
        1. علی بهزادیان نژاد نویسنده

          ستون‌های انتخابی در آرایه columns تعریف شده‌اند. * یعنی همه ستون‌ها. از Id برای انتخاب استفاده می‌کنیم. به زبان خودمانی یعنی همه ستون‌های رکوردی که Id آن برابر با مقدار داده شده است را انتخاب کن.

          پاسخ
  5. hossein

    با سلام. آموزشتون فوق العاده بود. ممنون.

    ولی من توی آخر کار به یه مشکل بر خوردم. اونم اینه که توی personlistadapter آندروید استودیو قسمت های
    persons.get(position).getId;
    persons.get(position).getName()
    persons.get(position).getFamily()

    اون تیکه های آخر، یعنی get id و name و fa,ily رو ارور میگیره. و هیچ پیشنهادی هم نمیده.
    گفتم شاید مشکل از کار خودم باشه، واسه همین کد های شما رو از گیت هاب گرفتم. ولی بازم بدون هیچ دست کاری میبینم که همون قسمتا ارور میده. ممنون میشم کمک کنید.

    با تشکر…

    پاسخ
    1. علی بهزادیان نژاد نویسنده

      کدی که تو گیت هابه هیچ مشکلی نداره. احتمالا تو کلاس Person متدهای set و get رو درست تعریف نکردید. بهتره برای ساخت متدهای set و get از خود اندروید استودیو استفاده کنید تا هم راحت باشید و هم کد استادندارد باشه و به مشکل نخوره. برای این کار بعد از تعریف فیلدهای کلاس، کلید ترکیبی Shift+Insert رو بزنید و از منوی باز شده ایجاد متدهای get و set رو انتخاب کنید تا همه متدها رو به صورت استاندارد براتون بسازه.

      پاسخ
  6. محمود

    سلام. با تشکر از مطالب بسیار خوبتون
    اگه بخوایم دو یا چند دیتابیس تعریف کنیم و داخل هر کدوم مثلاً دو یا چند جدول اون وقت آیا برای هر دیتابیس یا هر جدول باید یک کلاس جدا تو قسمت model ایجاد کنیم یا این که همه رو میتونیم تو یه کلاس تعریف کنیم؟ اگه می‌تونیم، چه طوری؟

    پاسخ
    1. علی بهزادیان نژاد نویسنده

      معمولا کلاس‌های مدل معادل جدول (table) های دیتابیس هستند. اگر بیشتر از یک دیتابیس داشته باشید موقع عملیات دیتابیس باید معلوم کنید با کدوم دیتابیس می‌خواهید کار کنید.

      پاسخ
  7. Ali

    سلام مرسی مطالب خوبتون
    میشه بگید چه جوری از کلاس readperson استفاده کنیم
    البته من نمی خوام ای دی رو پیدا کنه میخوام name رو پیدا کنم ؟
    با تشکر

    پاسخ

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

شما می‌توانید از این دستورات HTML استفاده کنید: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>