ذخیره داده ها در پایگاه داده برای داده های تکراری یا ساختار یافته، مانند اطلاعات تماس، ایده آل است. این صفحه فرض می کند که شما به طور کلی با پایگاه های داده SQL آشنا هستید و به شما کمک می کند تا با پایگاه های داده SQLite در اندروید شروع کنید. APIهایی که برای استفاده از پایگاه داده در Android نیاز دارید در بسته android.database.sqlite
موجود هستند.
احتیاط: اگرچه این APIها قدرتمند هستند، اما نسبتاً سطح پایینی دارند و برای استفاده به زمان و تلاش زیادی نیاز دارند:
- هیچ تأیید زمان کامپایل پرس و جوهای SQL خام وجود ندارد. همانطور که نمودار داده شما تغییر می کند، باید پرس و جوهای SQL آسیب دیده را به صورت دستی به روز کنید. این فرآیند می تواند زمان بر و مستعد خطا باشد.
- برای تبدیل بین پرس و جوهای SQL و اشیاء داده باید از کدهای boilerplate زیادی استفاده کنید.
به این دلایل، ما به شدت توصیه میکنیم که از Room Persistence Library به عنوان یک لایه انتزاعی برای دسترسی به اطلاعات در پایگاههای داده SQLite برنامه خود استفاده کنید.
طرح و قرارداد را تعریف کنید
یکی از اصول اصلی پایگاه های داده SQL طرح واره است: یک اعلام رسمی از نحوه سازماندهی پایگاه داده. این طرح در دستورات SQL که برای ایجاد پایگاه داده خود استفاده می کنید منعکس می شود. ممکن است برای شما مفید باشد که یک کلاس همراه، به نام کلاس قرارداد ، که به صراحت طرح طرح شما را به روشی سیستماتیک و مستندسازی مشخص می کند، مفید باشد.
کلاس قرارداد محفظه ای برای ثابت هاست که نام URI ها، جداول و ستون ها را تعریف می کند. کلاس قرارداد به شما امکان می دهد از ثابت های یکسانی در تمام کلاس های دیگر در همان بسته استفاده کنید. این به شما امکان می دهد نام ستون را در یک مکان تغییر دهید و آن را در سراسر کد خود منتشر کنید.
یک راه خوب برای سازماندهی کلاس قرارداد این است که تعاریفی را که برای کل پایگاه داده شما جهانی هستند در سطح ریشه کلاس قرار دهید. سپس برای هر جدول یک کلاس داخلی ایجاد کنید. هر کلاس داخلی ستون های جدول مربوطه را بر می شمارد.
توجه: با پیاده سازی رابط BaseColumns
، کلاس داخلی شما می تواند یک فیلد کلید اصلی به نام _ID
را که برخی از کلاس های اندروید مانند CursorAdapter
انتظار دارند، به ارث ببرند. لازم نیست، اما این می تواند به پایگاه داده شما کمک کند تا با فریم ورک اندروید هماهنگ باشد.
به عنوان مثال، قرارداد زیر نام جدول و نام ستون را برای یک جدول واحد که یک فید RSS را نشان می دهد، تعریف می کند:
کاتلین
object FeedReaderContract { // Table contents are grouped together in an anonymous object. object FeedEntry : BaseColumns { const val TABLE_NAME = "entry" const val COLUMN_NAME_TITLE = "title" const val COLUMN_NAME_SUBTITLE = "subtitle" } }
جاوا
public final class FeedReaderContract { // To prevent someone from accidentally instantiating the contract class, // make the constructor private. private FeedReaderContract() {} /* Inner class that defines the table contents */ public static class FeedEntry implements BaseColumns { public static final String TABLE_NAME = "entry"; public static final String COLUMN_NAME_TITLE = "title"; public static final String COLUMN_NAME_SUBTITLE = "subtitle"; } }
با استفاده از SQL helper یک پایگاه داده ایجاد کنید
هنگامی که نحوه ظاهر پایگاه داده خود را مشخص کردید، باید روش هایی را برای ایجاد و نگهداری پایگاه داده و جداول پیاده سازی کنید. در اینجا چند عبارت معمولی وجود دارد که یک جدول را ایجاد و حذف می کند:
کاتلین
private const val SQL_CREATE_ENTRIES = "CREATE TABLE ${FeedEntry.TABLE_NAME} (" + "${BaseColumns._ID} INTEGER PRIMARY KEY," + "${FeedEntry.COLUMN_NAME_TITLE} TEXT," + "${FeedEntry.COLUMN_NAME_SUBTITLE} TEXT)" private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${FeedEntry.TABLE_NAME}"
جاوا
private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + FeedEntry._ID + " INTEGER PRIMARY KEY," + FeedEntry.COLUMN_NAME_TITLE + " TEXT," + FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)"; private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
درست مانند فایلهایی که در حافظه داخلی دستگاه ذخیره میکنید، Android نیز پایگاه داده شما را در پوشه خصوصی برنامه شما ذخیره میکند. دادههای شما ایمن هستند، زیرا بهطور پیشفرض این منطقه برای سایر برنامهها یا کاربر قابل دسترسی نیست.
کلاس SQLiteOpenHelper
شامل مجموعه ای مفید از APIها برای مدیریت پایگاه داده شما است. هنگامی که از این کلاس برای به دست آوردن ارجاع به پایگاه داده خود استفاده می کنید، سیستم عملیات بالقوه طولانی مدت ایجاد و به روز رسانی پایگاه داده را فقط در صورت نیاز و نه در هنگام راه اندازی برنامه انجام می دهد. تنها کاری که باید انجام دهید این است که getWritableDatabase()
یا getReadableDatabase()
را فراخوانی کنید.
توجه: از آنجا که آنها می توانند طولانی مدت باشند، مطمئن شوید که getWritableDatabase()
یا getReadableDatabase()
را در یک رشته پس زمینه فراخوانی کنید. برای اطلاعات بیشتر به Threading در اندروید مراجعه کنید.
برای استفاده از SQLiteOpenHelper
، یک زیرکلاس ایجاد کنید که متدهای onCreate()
و onUpgrade()
را باطل می کند. همچنین ممکن است بخواهید متدهای onDowngrade()
یا onOpen()
را پیاده سازی کنید، اما نیازی به آنها نیست.
به عنوان مثال، در اینجا پیاده سازی SQLiteOpenHelper
است که از برخی از دستورات نشان داده شده در بالا استفاده می کند:
کاتلین
class FeedReaderDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { override fun onCreate(db: SQLiteDatabase) { db.execSQL(SQL_CREATE_ENTRIES) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES) onCreate(db) } override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { onUpgrade(db, oldVersion, newVersion) } companion object { // If you change the database schema, you must increment the database version. const val DATABASE_VERSION = 1 const val DATABASE_NAME = "FeedReader.db" } }
جاوا
public class FeedReaderDbHelper extends SQLiteOpenHelper { // If you change the database schema, you must increment the database version. public static final int DATABASE_VERSION = 1; public static final String DATABASE_NAME = "FeedReader.db"; public FeedReaderDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(db, oldVersion, newVersion); } }
برای دسترسی به پایگاه داده خود، زیر کلاس SQLiteOpenHelper
خود را نمونه سازی کنید:
کاتلین
val dbHelper = FeedReaderDbHelper(context)
جاوا
FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());
اطلاعات را در یک پایگاه داده قرار دهید
با ارسال یک شی ContentValues
به متد insert()
داده ها را در پایگاه داده وارد کنید:
کاتلین
// Gets the data repository in write mode val db = dbHelper.writableDatabase // Create a new map of values, where column names are the keys val values = ContentValues().apply { put(FeedEntry.COLUMN_NAME_TITLE, title) put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle) } // Insert the new row, returning the primary key value of the new row val newRowId = db?.insert(FeedEntry.TABLE_NAME, null, values)
جاوا
// Gets the data repository in write mode SQLiteDatabase db = dbHelper.getWritableDatabase(); // Create a new map of values, where column names are the keys ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_TITLE, title); values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle); // Insert the new row, returning the primary key value of the new row long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
اولین آرگومان برای insert()
به سادگی نام جدول است.
آرگومان دوم به فریمورک می گوید که در صورت خالی بودن ContentValues
چه کاری انجام دهد (یعنی شما هیچ مقداری put
نداده اید). اگر نام یک ستون را مشخص کنید، فریمورک یک ردیف را وارد می کند و مقدار آن ستون را null می کند. اگر null
مشخص کنید، مانند این نمونه کد، فریم ورک وقتی هیچ مقداری وجود ندارد، ردیفی را وارد نمیکند.
متدهای insert()
شناسه ردیف تازه ایجاد شده را برمیگرداند، یا اگر در درج دادهها خطایی وجود داشته باشد، -1 را برمیگرداند. اگر با داده های از قبل موجود در پایگاه داده تضاد داشته باشید، ممکن است این اتفاق بیفتد.
خواندن اطلاعات از پایگاه داده
برای خواندن از پایگاه داده، از متد query()
استفاده کنید و معیارهای انتخاب و ستون های مورد نظر خود را به آن ارسال کنید. این متد عناصر insert()
و update()
را با هم ترکیب می کند، به جز اینکه لیست ستونی، داده هایی را که می خواهید واکشی کنید ("پروژه")، به جای داده هایی که قرار است درج کنید، تعریف می کند. نتایج پرس و جو در یک شی Cursor
به شما برگردانده می شود.
کاتلین
val db = dbHelper.readableDatabase // Define a projection that specifies which columns from the database // you will actually use after this query. val projection = arrayOf(BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE) // Filter results WHERE "title" = 'My Title' val selection = "${FeedEntry.COLUMN_NAME_TITLE} = ?" val selectionArgs = arrayOf("My Title") // How you want the results sorted in the resulting Cursor val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC" val cursor = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order )
جاوا
SQLiteDatabase db = dbHelper.getReadableDatabase(); // Define a projection that specifies which columns from the database // you will actually use after this query. String[] projection = { BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE }; // Filter results WHERE "title" = 'My Title' String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?"; String[] selectionArgs = { "My Title" }; // How you want the results sorted in the resulting Cursor String sortOrder = FeedEntry.COLUMN_NAME_SUBTITLE + " DESC"; Cursor cursor = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order );
آرگومان های سوم و چهارم ( selection
و selectionArgs
) برای ایجاد یک عبارت WHERE ترکیب می شوند. از آنجایی که آرگومان ها جدا از کوئری انتخاب ارائه می شوند، قبل از ترکیب شدن از آنها فرار می کنند. این باعث می شود که بیانیه های انتخاب شما در برابر تزریق SQL مصون باشند. برای جزئیات بیشتر در مورد همه آرگومان ها، به مرجع query()
مراجعه کنید.
برای مشاهده یک ردیف در مکان نما، از یکی از روش های حرکت Cursor
استفاده کنید، که همیشه باید قبل از شروع خواندن مقادیر، آن را فراخوانی کنید. از آنجایی که مکان نما از موقعیت -1 شروع می شود، فراخوانی moveToNext()
"موقعیت خواندن" را در اولین ورودی نتایج قرار می دهد و برمی گرداند که آیا مکان نما از آخرین ورودی مجموعه نتایج گذشته است یا نه. برای هر سطر، میتوانید مقدار یک ستون را با فراخوانی یکی از متدهای Cursor
get، مانند getString()
یا getLong()
بخوانید. برای هر یک از متدهای get، باید موقعیت شاخص ستون مورد نظر خود را پاس کنید، که می توانید با فراخوانی getColumnIndex()
یا getColumnIndexOrThrow()
دریافت کنید. پس از پایان یافتن تکرار در نتایج، برای آزاد کردن منابع آن، close()
روی مکان نما فراخوانی کنید. برای مثال، موارد زیر نشان میدهد که چگونه میتوان همه شناسههای آیتمهای ذخیرهشده در مکاننما را دریافت کرد و آنها را به فهرست اضافه کرد:
کاتلین
val itemIds = mutableListOf<Long>() with(cursor) { while (moveToNext()) { val itemId = getLong(getColumnIndexOrThrow(BaseColumns._ID)) itemIds.add(itemId) } } cursor.close()
جاوا
List itemIds = new ArrayList<>(); while(cursor.moveToNext()) { long itemId = cursor.getLong( cursor.getColumnIndexOrThrow(FeedEntry._ID)); itemIds.add(itemId); } cursor.close();
حذف اطلاعات از پایگاه داده
برای حذف ردیفها از جدول، باید معیارهای انتخابی را ارائه کنید که ردیفهای متد delete()
را مشخص کند. مکانیزم همانند آرگومان های انتخابی متد query()
کار می کند. مشخصات انتخاب را به یک بند انتخاب و آرگومان های انتخاب تقسیم می کند. این بند ستون هایی را که باید به آنها نگاه کنید را تعریف می کند و همچنین به شما اجازه می دهد تا تست های ستون را ترکیب کنید. آرگومانها مقادیری هستند که باید در برابر آنها آزمایش شوند که به عبارت محدود شدهاند. از آنجایی که نتیجه مانند یک عبارت SQL معمولی مدیریت نمی شود، از تزریق SQL مصون است.
کاتلین
// Define 'where' part of query. val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?" // Specify arguments in placeholder order. val selectionArgs = arrayOf("MyTitle") // Issue SQL statement. val deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs)
جاوا
// Define 'where' part of query. String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?"; // Specify arguments in placeholder order. String[] selectionArgs = { "MyTitle" }; // Issue SQL statement. int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);
مقدار بازگشتی برای متد delete()
نشان دهنده تعداد ردیف هایی است که از پایگاه داده حذف شده اند.
یک پایگاه داده را به روز کنید
هنگامی که نیاز به تغییر زیر مجموعه ای از مقادیر پایگاه داده خود دارید، از متد update()
استفاده کنید.
به روز رسانی جدول، نحو ContentValues
insert()
را با نحو WHERE
از delete()
ترکیب می کند.
کاتلین
val db = dbHelper.writableDatabase // New value for one column val title = "MyNewTitle" val values = ContentValues().apply { put(FeedEntry.COLUMN_NAME_TITLE, title) } // Which row to update, based on the title val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?" val selectionArgs = arrayOf("MyOldTitle") val count = db.update( FeedEntry.TABLE_NAME, values, selection, selectionArgs)
جاوا
SQLiteDatabase db = dbHelper.getWritableDatabase(); // New value for one column String title = "MyNewTitle"; ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_TITLE, title); // Which row to update, based on the title String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?"; String[] selectionArgs = { "MyOldTitle" }; int count = db.update( FeedReaderDbHelper.FeedEntry.TABLE_NAME, values, selection, selectionArgs);
مقدار بازگشتی متد update()
تعداد ردیف هایی است که در پایگاه داده تحت تأثیر قرار می گیرند.
اتصال پایدار به پایگاه داده
از آنجایی که هنگام بسته شدن پایگاه داده getWritableDatabase()
و getReadableDatabase()
گران هستند، باید اتصال پایگاه داده خود را تا زمانی که نیاز به دسترسی دارید باز بگذارید. به طور معمول، بستن پایگاه داده در onDestroy()
Activity فراخوانی بهینه است.
کاتلین
override fun onDestroy() { dbHelper.close() super.onDestroy() }
جاوا
@Override protected void onDestroy() { dbHelper.close(); super.onDestroy(); }
دیتابیس خود را دیباگ کنید
Android SDK شامل یک ابزار پوسته sqlite3
است که به شما امکان می دهد محتویات جدول را مرور کنید، دستورات SQL را اجرا کنید و سایر عملکردهای مفید را در پایگاه های داده SQLite انجام دهید. برای اطلاعات بیشتر، نحوه صدور دستورات پوسته را ببینید.