يعد حفظ البيانات في قاعدة بيانات أمرًا مثاليًا لتكرار البيانات المهيكلة،
مثل معلومات الاتصال. تفترض هذه الصفحة أنك
على دراية بقواعد بيانات SQL بشكل عام وتساعدك على البدء في
قواعد بيانات SQLite على Android. واجهات برمجة التطبيقات التي تحتاجها لاستخدام قاعدة بيانات
على أجهزة Android تتوفّر في حزمة android.database.sqlite
.
تحذير: على الرغم من فعالية واجهات برمجة التطبيقات هذه، إلا أنها ذات مستوى منخفض إلى حد ما. وتتطلب قدرًا كبيرًا من الوقت والجهد لاستخدامها:
- لا يتوفّر التحقّق في وقت التجميع لطلبات بحث SQL الأولية. استخدام البيانات في الرسم البياني، فستحتاج إلى تحديث استعلامات SQL (لغة الاستعلام البنيوية) المتأثرة يدويًا. هذا النمط يمكن أن تستغرق وقتًا طويلاً وتكون عرضة للخطأ.
- تحتاج إلى استخدام الكثير من التعليمات البرمجية النموذجية للتحويل بين استعلامات SQL وعناصر البيانات.
لهذه الأسباب، ننصح بشدة باستخدام مكتبة استمرارية الغرفة كطبقة تجريدية للوصول إلى المعلومات في SQLite في تطبيقك لقواعد البيانات.
تحديد المخطط والعقد
أحد المبادئ الرئيسية لقواعد بيانات SQL هو المخطط: النهج الرسمي عن كيفية تنظيم قاعدة البيانات. ينعكس المخطط في لغة الاستعلامات البنيوية (SQL) العبارات التي تستخدمها لإنشاء قاعدة البيانات الخاصة بك. قد تجد أنه من المفيد إنشاء فئة مصاحبة، تُعرف باسم فئة عقد، والتي تحدّد صراحةً تخطيط المخطط بطريقة منهجية وتوثيقية.
فئة العقد هي حاوية للثوابت التي تحدّد أسماء لعناوين URL والجداول والأعمدة. تتيح لك فئة العقد استخدام نفس الثوابت عبر جميع الفئات الأخرى في نفس الحزمة. يتيح لك هذا الإجراء تغيير عمود الاسم في مكان واحد واجعله ينتشر عبر التعليمات البرمجية.
من الطرق الجيدة لتنظيم فئة العقد وضع تعريفات عامة إلى قاعدة البيانات بأكملها في المستوى الجذر للفئة. بعد ذلك، أنشئ فئة داخلية لكل جدول. تعرِض كل فئة داخلية أعمدة الجدول المقابل.
ملاحظة: من خلال تنفيذ BaseColumns
فئتك الداخلية، يمكن أن تكتسب فئة أساسية
يسمى _ID
الذي تتوقعه بعض فئات Android مثل CursorAdapter
. ليس هذا الإجراء مطلوبًا، ولكن يمكن أن يساعد قاعدة بياناتك في العمل بشكل متناغم مع إطار عمل Android.
على سبيل المثال، يحدد العقد التالي اسم الجدول وأسماء الأعمدة جدول فردي يمثّل خلاصة RSS:
Kotlin
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" } }
Java
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
بمجرد تحديد شكل قاعدة البيانات الخاصة بك، يجب عليك تنفيذ الطرق التي تنشئ وتحافظ على قاعدة البيانات والجداول. في ما يلي بعض الجمل النموذجية التي تنشئ جدولاً وتحذفه:
Kotlin
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}"
Java
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
على
مجموعة من واجهات برمجة التطبيقات لإدارة قاعدة البيانات لديك.
عند استخدام هذه الفئة للحصول على مراجع لقاعدة بياناتك،
ينفذ الإجراءات المحتملة
عمليات طويلة الأمد لإنشاء قاعدة البيانات وتحديثها فقط عندما
مطلوبة وليس أثناء بدء تشغيل التطبيق. ما عليك سوى الاتصال بالرقم
getWritableDatabase()
أو
getReadableDatabase()
.
ملاحظة: بما أنّ هذه المعاملات قد تستغرق وقتًا طويلاً،
تأكَّد من طلب getWritableDatabase()
أو getReadableDatabase()
في سلسلة محادثات في الخلفية.
يُرجى الاطّلاع على مقالة Threading على Android للحصول على مزيد من المعلومات.
لاستخدام SQLiteOpenHelper
، أنشئ فئة فرعية تلغي onCreate()
وonUpgrade()
. يمكنك
تريد أيضًا تنفيذ
onDowngrade()
أو
onOpen()
طرق،
لكنها ليست مطلوبة.
على سبيل المثال، إليك تنفيذ SQLiteOpenHelper
بعض الأوامر الموضحة أعلاه:
Kotlin
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" } }
Java
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
:
Kotlin
val dbHelper = FeedReaderDbHelper(context)
Java
FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());
وضع المعلومات في قاعدة بيانات
إدراج البيانات في قاعدة البيانات من خلال إدخال علامة ContentValues
كائن إلى الطريقة insert()
وهي:
Kotlin
// 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)
Java
// 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
، كما في هذه
عينة التعليمات البرمجية، لا يُدرج إطار العمل صفًا في حالة عدم وجود قيم.
تُرجع الطرق insert()
معرّف
حديثًا، وإلا فسيتم عرض -1 إذا كان هناك خطأ في إدراج البيانات. يمكن أن يحدث هذا
في حالة وجود تعارض مع بيانات موجودة مسبقًا في قاعدة البيانات.
قراءة معلومات من قاعدة بيانات
للقراءة من قاعدة بيانات، استخدِم الطريقة query()
، مع تمرير معايير الاختيار والأعمدة المطلوبة إليها.
تجمع الطريقة بين عناصر insert()
وupdate()
، باستثناء قائمة الأعمدة
تحدد البيانات التي تريد جلبها ("الإسقاط")، بدلاً من البيانات المراد إدراجها. النتائج
طلب البحث في كائن Cursor
.
Kotlin
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 )
Java
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()
إلى وضع "موضع القراءة" في صفحة
الإدخال الأول في النتائج ويعرض ما إذا كان المؤشر قد تجاوز الإدخال الأخير بالفعل أم لا
مجموعة النتائج. بالنسبة إلى كل صف، يمكنك قراءة قيمة عمود من خلال استدعاء إحدى methods
Cursor
للحصول على القيمة، مثل getString()
أو getLong()
. لكل طريقة من طرق get،
يجب أن تجتاز موضع الفهرس للعمود الذي تريده، والذي يمكنك الحصول عليه من خلال استدعاء
getColumnIndex()
أو
getColumnIndexOrThrow()
عند الانتهاء
تكرار النتائج، طلب close()
في المؤشر
إطلاق مواردها.
على سبيل المثال، يوضّح ما يلي كيفية الحصول على جميع معرّفات السلع المخزّنة في مؤشر
وإضافته إلى القائمة:
Kotlin
val itemIds = mutableListOf<Long>() with(cursor) { while (moveToNext()) { val itemId = getLong(getColumnIndexOrThrow(BaseColumns._ID)) itemIds.add(itemId) } } cursor.close()
Java
List itemIds = new ArrayList<>(); while(cursor.moveToNext()) { long itemId = cursor.getLong( cursor.getColumnIndexOrThrow(FeedEntry._ID)); itemIds.add(itemId); } cursor.close();
حذف معلومات من قاعدة بيانات
لحذف صفوف من جدول، يلزمك تقديم معايير التحديد التي
لتحديد الصفوف بالطريقة delete()
. تعمل الآلية بالطريقة نفسها التي تعمل بها وسيطة الاختيار في الأسلوب
query()
. يقسم
مواصفات الاختيار في عبارة اختيار ووسيطات اختيار. تشير رسالة الأشكال البيانية
تحدد الأعمدة التي يجب النظر إليها، وتسمح لك أيضًا بدمج الأعمدة
الاختبار. الوسيطات هي قيم لاختبارها ومرتبطة بالعبارة.
ولأنّه لا يتم التعامل مع النتيجة بالطريقة نفسها التي يتم بها التعامل مع عبارة SQL عادية، فهي
محصَّنة من هجمات حقن SQL.
Kotlin
// 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)
Java
// 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()
.
Kotlin
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)
Java
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()
من نشاط الاتصال.
Kotlin
override fun onDestroy() { dbHelper.close() super.onDestroy() }
Java
@Override protected void onDestroy() { dbHelper.close(); super.onDestroy(); }
تصحيح أخطاء قاعدة البيانات
تتضمّن حزمة تطوير البرامج (SDK) لنظام التشغيل Android أداة واجهة sqlite3
تتيح لك التصفّح.
وتشغيل أوامر SQL وتنفيذ دوال مفيدة أخرى على SQLite
لقواعد البيانات. لمزيد من المعلومات، يُرجى الاطّلاع على طريقة طريقة إصدار أوامر واجهة الأوامر.