در این قسمت که در ادامه قسمت قبلی است قصد داریم مبحث پایگاه داده اندروید را تکمیل کنیم.
اگر با مفهوم پایگاه داده آشنا نیستید مطلب آشنایی با پایگاه داده را از دست ندهید.
دریافت اطلاعات از دیتابیس SQlite اندروید
در این بخش قصد داریم متد getPosts را به کلاس HitosSQLiteOpenHelper اضافه کنیم.
public List<Posts> getPosts(){
SQLiteDatabase sqLiteDatabase = this.getReadableDatabase();
Cursor cursor= sqLiteDatabase.rawQuery("SELECT * FROM POSTS", null);
cursor.moveToFirst();
List<Posts> posts = new ArrayList<>();
if(cursor.getCount() > 0){
while (!cursor.isAfterLast()){
Posts post = new Posts();
post.setId(cursor.getInt(0));
post.setTitle(cursor.getString(1));
post.setIntro(cursor.getString(2));
post.setFullPost(cursor.getString(3));
post.setDate(cursor.getString(4));
post.setImage(cursor.getString(5));
posts.add(post);
cursor.moveToNext();
}
}
cursor.close();
sqLiteDatabase.close();
return posts;
}
متد بالا تمام اطلاعات موجود در جدول POSTS را استخراج و ارسال میکند. تشریح کدهای این بخش عبارت است از:
سطر 1: با توجه به List<Posts>
، خروجی این متد یک List از نوع Postsها است.
سطر 2: یک شی از SQLiteDatabase به نام sqLiteDatabase ایجاد میکنیم. به این دلیل که در این متد تنها قصد داریم پستها را بخوانیم از getReadableDatabase استفاده میکنیم.
سطر 3: با استفاده از شی sqLiteDatabase و دستور rawQuery یک دستور SQL را در پایگاه داده اجرا میکنیم و نتایج را در یک شی به نام cursor از نوع Cursor قرار میدهیم. دستور SQL به شرح زیر است:
SELECT *
: انتخاب کلیه موارد با این دستور انجام میپذیرد. میتوان به جای ستاره موارد دریافتی را محدود کرد. مثلا اگر از SELECT id, intro
استفاده کنیم تنها idها و introها دریافت میشوند.
FROM POSTS
: مشخص کننده مکان دریافت اطلاعات است که در این جا جدول POSTS است.
سطر 4: شی cursor را با دستور moveToFirst به ابتدای آیتمهای جدول POSTS میفرستیم.
سطر 5: یک List از ArrayListهای از نوع Posts را در متغیر posts ذخیره میکنیم.
سطر 7: بررسی میکنیم اگر تعداد اطلاعات دریافتی بزرگتر از صفر باشد وارد حلقه پردازش اطلاعات میشویم.
سطر 8: یک حلقه از نوع while ایجاد میکنیم که شرط اتمام حلقه بر قرار نبودن false بودن cursor.isAfterLast()
است. این دستور بررسی میکند که آیا cursor به آخرین آیتم رسیده است یا خیر.
سطرهای 9 تا 16: در این سطرها یک شی به نام post از نوع Posts ایجاد کرده و کلیه اطلاعات دریافتی از cursor را درون متغیری از post و در نهایت در متغیر posts سطر 5 میگذاریم.
سطر 17: با دستور cursor.moveToNext()
به cursor بعدی میرویم.
سطرهای 20 و 21: در اتمام استفاده از هر Cursor و یا SQLiteDatabase باید آنها را با دستور close ببندیم.
سطر 22: کلیه اطلاعات ذخیره شده در posts را از تابع خارج میکنیم.
در MainActivity و متد onCreate کدهای زیر را اضافه میکنیم:
HitosSQLiteOpenHelper hitosSQLiteOpenHelper = new HitosSQLiteOpenHelper(this);
List<Posts> posts= hitosSQLiteOpenHelper.getPosts();
for (int i= 0; i < posts.size(); i++){
Log.i("PostNum-", "" + posts.get(i).getId() + ": " + posts.get(i).getTitle() + ": " + posts.get(i).getIntro());
}
سطر 1: یک شی از روی کلاس HitosSQLiteOpenHelper ایجاد میکنیم.
سطر 2: با استفاده از شی ایجاد شده در سطر 1 اطلاعات دریافت شده از متد getPosts را درون یک متغیر به نام posts از نوع List<Posts>
قرار میدهیم.
سطر 3: یک حلقه از نوع for را به تعداد posts.size()
اجرا میکنیم تا کلیه اطلاعات موجود در جدول پیمایش شوند.
سطر 4: با استفاده از Log.i سعی در چاپ کردن id و title و intro در لاگ داریم.
نتیجه اجرای کدهای فوق در Android Monitor اندروید استودیو نمایان خواهد شد.
دریافت بخشی از اطلاعات دیتابیس برنامه
در مثال بالا گفتیم دستورات موجود در ورودی rawQuery باعث فراخوانی اطلاعات میشوند.
در مثال قبل با دستور زیر کلیه اطلاعات موجود در جدول POSTS را دریافت کردیم:
Cursor cursor= sqLiteDatabase.rawQuery("SELECT * FROM POSTS", null);
اگر قصد داشته باشیم اطلاعاتی را دریافت کنیم که title آنها دقیقا برابر "عنوان اولین مطلب" باشد باید چگونه عمل کنیم؟
ورودیهای متغیر rawQuery را به شکل زیر ویرایش کنید:
Cursor cursor= sqLiteDatabase.rawQuery("SELECT * FROM POSTS WHERE title = 'عنوان اولین مطلب'", null);
عبارت WHERE برای اعمال فیلتر بر نتایج rawQuery کاربرد دارد. پس از WHERE عبارت title و سپس علامت مساوی و در نهایت رشته مورد نظر را داخل دو علامت نقل قول تکی وارد کردیم. اگر در پایگاه داده خود یک title با عبارت "عنوان اولین مطلب" موجود بود اطلاعات به سمت شما باز میگردد.
حال میتوان این فیلتر را هوشمند کرد و مطالبی را جستجو کرد که title آنها حاوی عبارت خاصی باشد. مثلا:
Cursor cursor= sqLiteDatabase.rawQuery("SELECT * FROM POSTS WHERE title LIKE '%مطلب%'", null);
در مثال بالا تمام اطلاعاتی از جدول POSTS که title آنها شامل عبارت "مطلب" هستند فراخوانی خواهند شد.
ورودی دوم دستور rawQuery برای پیاده سازی پارامترهای فیلتر فوق کاربرد دارد. مثلا:
Cursor cursor= sqLiteDatabase.rawQuery("SELECT * FROM POSTS WHERE title LIKE ?", new String[]{"%مطلب%"});
در بالا علامت سوال موجود در رشته از آرایه موجود در ورودی دوم دریافت شد.
برای داشتن چند پارامتر به صورت زیر عمل میکنیم:
Cursor cursor= sqLiteDatabase.rawQuery("SELECT * FROM POSTS WHERE title LIKE ? or title LIKE ?", new String[]{"%اولین%", "%دومین%"});
عبارت or به معنای "یا" است و همانطور که در مباحث شرطهای جاوا گفتیم به گونهای عمل میکند که یا شرط قبل و یا بعد و یا هر دو شرط قبل و بعدش مورد پذیرش باشند.
جلوگیری از بار گذاری اطلاعات تکراری
در مثال پیاده سازی شده در قسمت قبل هر باری که برنامه را اجرا کنیم کلیه آیتمها در جدول پایگاه داده ذخیره میشوند، در صورتی که اطلاعات تنها یک بار باید ذخیره شوند و نباید اطلاعات تکراری در پایگاه داده خود داشته باشیم.
برای حل این مشکل باید پیش از ذخیر اطلاعات در دیتابیس مطمئن شویم که این اطلاعات پیش از این در دیتابیس ذخیره شده اند یا خیر. بدین منظور متد savePosts را به شکل زیر ویرایش میکنیم:
public Boolean savePosts(List<Posts> posts){
SQLiteDatabase sqLiteDatabase=this.getWritableDatabase();
for (int i = 0; i < posts.size(); i++){
Cursor cursor= sqLiteDatabase.rawQuery("SELECT * FROM POSTS WHERE title = ?", new String[]{posts.get(i).getTitle()});
if (cursor.getCount() == 0){
ContentValues cv=new ContentValues();
cv.put("title",posts.get(i).getTitle());
cv.put("intro",posts.get(i).getIntro());
cv.put("fullPost",posts.get(i).getFullPost());
cv.put("date",posts.get(i).getDate());
cv.put("image",posts.get(i).getImage());
long isInserted=sqLiteDatabase.insert("POSTS" , null, cv);
if(isInserted > 0){
Log.i("Insert_DB", "Post Id: "+isInserted);
}else{
Log.i("Insert_DB", "Error!");
}
}
}
Cursor cursor= sqLiteDatabase.rawQuery("SELECT * FROM POSTS", null);
Log.i("PostItemsCount", "PostItemsCount: " + cursor.getCount());
return true;
}
سطر 4: در دیتابیس با دستور SQL جستجو میکنیم که پستی که دریافت شده است قبلا در دیتابیس موجود بوده است یا خیر.
سطر 5: اگر cursor.getCount()
برابر صفر بود یعنی اطلاعاتی پیش از این در دیتابیس ذخیره نشده است، و اطلاعات درون دیتابیس ذخیره میشوند.
سطر 23: یک لاگ به نام PostItemsCount تعداد اطلاعات موجود در دیتابیس را نمایش میدهد تا مطمین شوید چه تعداد اطلاعات در پایگاه داده POSTS دارید.
بروز رسانی اطلاعات دیتابیس
طبیعتا گاهی نیاز است اطلاعات موجود در پایگاه داده بروز رسانی شوند. مثلا فرض کنید در مثال قبل قصد دارید تاریخ یک مورد خاص را در مطالب ویرایش کنید.
یک متد به نام updateDate در کلاس HitosSQLiteOpenHelper با سورس زیر اضافه میکنیم:
public void updateDate(String title, String newDate){
ContentValues cv = new ContentValues();
cv.put("date", newDate);
SQLiteDatabase sqLiteDatabase=this.getWritableDatabase();
int count= sqLiteDatabase.update("POSTS", cv, "title = '"+title + "'", null);
Log.i("update_posts", "" + count);
}
سطر 1: ورودیهای این متد عنوان و تاریخ جدید است. و قصد داریم تاریخ پستی از دیتابیس با title مشخص را ویرایش کنیم.
سطر 2: یک ContentValues با نام cv ایجاد میکنیم.
سطر 3: با دستور put مقدار date را برابر newDate قرار میدهیم.
سطر 4: یک شی از روی SQLiteDatabase ایجاد میکنیم.
سطر 5: با شی sqLiteDatabase و دستور put با چهار ورودی ردیف مورد نظر پایگاه داده را یافته و ویرایش میکنیم. ورودی اول نام جدول، ورودی دوم ContentValues و ورودی سوم شرط و ورودی چهارم null است.
ورودیهای دستور update را به صورت زیر نیز میتوان نوشت:
int count= sqLiteDatabase.update("POSTS", cv, "title = ?", new String[]{title});
تعداد آیتمهای ویرایش شده نیز در متغیر count ذخیره میشود که در Log موجود در سطر 6 به نمایش در میآید.
پاک کردن اطلاعات از دیتابیس
پاک کردن اطلاعات از دیتابیس عمل راحتی است و تنها کافی است یک متد مانند زیر به کلاس HitosSQLiteOpenHelper اضافه کنید:
public void deletePosts(String title){
SQLiteDatabase sqLiteDatabase=this.getWritableDatabase();
int count= sqLiteDatabase.delete("POSTS", "title = ?", new String[]{title});
Log.i("delete_posts", "" + count);
}
این متد عملکرد سادهای دارد و با استفاده از دستور delete و سه ورودی نام جدول، شرط و مقادیر شرط به سادگی میتواند اطلاعات را پاک کند. متغیر count تعداد آیتمهای حذف شده را در خود نگاه میدارد.
متد onCreate کلاس MainActivity را به شرح زیر ویرایش میکنیم:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HitosSQLiteOpenHelper openHelper = new HitosSQLiteOpenHelper(MainActivity.this);
openHelper.deletePosts("عنوان اولین مطلب");
}
با ارسال عنوان پست مورد نظر به کلاس HitosSQLiteOpenHelper به سادگی اطلاعات را پاک میکنیم.
استفاده از دیتابیس پیش ساخته و انتقال آن به برنامه اندروید
همانطور که در اول قسمت قبل گفتیم روش دومی برای استفاده از دیتابیس در برنامه اندروید وجود دارد و آن هم این است که پایگاه داده را در کامپیوتر خود ایجاد و به پوشه Assets پروژه اندروید خود منتقل کنیم تا پس از اولین اجرا دیتابیس از Assets به پوشه database پکیج پروژه منتقل شود.
در بالای نرم افزار DB Browser for SQLite روی File روی New Database کلیک کنید.
یک مکان برای ذخیره پایگاه داده مشخص کرده و نام پایگاه داده خود را مانند hitos.sqlite وارد کنید.
در بالای کادر باز شده ابتدا نام پایگاه داده خود را وارد کرده و سپس با کلیک روی Add field یک آیتم جدید برای پایگاه داده خود اضافه کنید:
با کلیک روی Add field یک آیتم جدید در کادر Fields ایجاد میگردد. در این کادر میتوان نام آیتم را انتخاب و پنج خاصیت آن را تنظیم کرد. این پنج خاصیت عبارتند از:
Type: نوع داده را مشخص میکند که در بیشتر حالات یا INTEGER و یا TEXT به کار شما میآید، INTEGER برای ذخیره ارقام و TEXT برای ذخیره نوشتهها هستند.
Not: خلاصه NOT NULL است و شما را مجبور میکند در حین قرار دادن اطلاعات حتما این فیلد را پر کنید.
PK: خلاصه Primary Key است و به معنای کلید است. معمولا هر جدول برای هر ردیف خود یک Primary Key دارد، این Primary Key میتواند شناسه نیز تعبیر شود، مثلا اگر در این جدول اطلاعات دانشجویان را ذخیره میکنید Primary Key این جدول شماره دانشجویی افراد است.
AI: خلاصه Auto Increment به معنای افزایش خودار است. مثلا اگر فیلد قبلی جدول دو باشد فیلد بعدی قطعا سه خواهد بود و به صورت خودکار افزایش مییابد.
U: خلاصه unique است و به این معناست که در این فیلد مقداری منحصر به فرد نسبت به سایر مقادیر ذخیره میشود. مثلا فرض کنید در فیلدی که unique آن فعال باشد میتوان شناسه را ذخیره کرد تا از منحصر به فرد بودن آن با سایر ستونها اطمینان حاصل کرد.
Default: مقدار پیش فرض را برای این فیلد تعیین میکند و به این معناست که اگر در ذخیره اطلاعات جدول این فیلد را مشخص نکنید مقدار پیش فرض در آن ذخیره خواهد شد.
نکته: اگر آیتم شناسه ID دارید تمام تیکها را بزنید.
با کلیک مجدد روی Add field آیتمهای دیگر را به جدول خود اضافه کنید.
پس از تشکیل جدول خود روی Browse Data کلیک کرده و اطلاعات جدید را به پایگاه داده خود اضافه کنید. برای اضافه کردن اطلاعات روی New Recod کلیک کنید.
پس از اتمام عملیات روی کلید Write Changes کلیک کنید تا تغییرات در پایگاه داده ذخیره شود.
به پوشه پروژه خود رفته و پوشههای زیر را پیمایش کنید:
app\src\main
در این مسیر یک پوشه به نام assets ایجاد کرده و درون این پوشه یک پوشه دیگر به نام databases ایجاد کنید و پایگاه داده را در آن قرار دهید. خواهید دید این فایل در اندروید استودیو نیز قابل مشاهده خواهد بود.
برای انجام عملیات انتقال پایگاه داده به یک کتابخانه قدرتمند به نام Android SQLiteAssetHelper نیازمندید. به مسیر File و Project Structure بروید و از آن app و Dependencies را انتخاب کنید. روی علامت مثبت و در نهایت Library Dependency کلیک کنید.
در کادر جستجو عبارت com.readystatesoftware.sqliteasset:sqliteassethelper را جستجو کنید، و پس از یافتن آن روی کلیک کرده و OK را فشار دهید تا به کتابخانههای پروژه شما افزوده شود.
فایل HitosSQLiteOpenHelper را مانند زیر ویرایش کنید:
public class HitosSQLiteOpenHelper extends SQLiteOpenHelper {
Context context;
public static String DB_NAME = "hitos.sqlite";
public static int DB_VERSION = 1;
public HitosSQLiteOpenHelper(Context context){
super(context, DB_NAME, null, DB_VERSION);
this.context = context;
}
@Override
public void onCreate(SQLiteDatabase db){
DataBaseConnection dbs = new DataBaseConnection(context);
SQLiteDatabase database = dbs.getReadableDatabase();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){}
public class DataBaseConnection extends SQLiteAssetHelper {
public DataBaseConnection(Context context) {
super(context, DB_NAME, null, DB_VERSION);
setForcedUpgrade(DB_VERSION);
}
}
}
همانطور که در بالا میبینید به انتهای کلاس، یک کلاس جدید به نام DataBaseConnection که ارث برده شده از SQLiteAssetHelper است را اضافه نمودیم.
سورسهای درون متد onCreate را ویرایش کردیم تا از کلاس DataBaseConnection استفاده کرده و دیتابیس را یکبار فراخوانی کند.
طبیعتا مثل مطالب ابتدای این قسمت و قسمت قبل میتوانید به راحتی از متدهایی برای ذخیره و دریافت اطلاعات درون این کلاس استفاده کنید.
امیدوارم این مطلب برای شما مفید بوده باشد. سعی کنید کدهای نوشته شده در این آموزش را چند بار تمرین کنید.