مثال على تعليمات SQLite Database في Android

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

Android SQLite

تعد SQLite لنظام Android قاعدة بيانات خفيفة للغاية والتي تأتي مع نظام Android. تجمع SQLite لنظام Android بين واجهة SQL نظيفة مع بصمة ذاكرة صغيرة للغاية وسرعة مقبولة. بالنسبة لنظام Android، تعتبر SQLite “مُدمجة في” تشغيل Android، بحيث يمكن لكل تطبيق Android إنشاء قواعد بيانات SQLite الخاصة به. واجهة برمجة تطبيقات SQLite الأصلية لنظام Android ليست JDBC، حيث أن JDBC قد يكون زيادة في العبء على الذاكرة لهاتف ذكي ذا مساحة ذاكرة محدودة. بمجرد إنشاء قاعدة بيانات بنجاح، يتم تحديدها في data/data//databases/ ويمكن الوصول إليها من خلال مراقب جهاز Android. تعتبر SQLite قاعدة بيانات علاقية نموذجية، تحتوي على جداول (تتألف من صفوف وأعمدة)، فهارس، إلخ. يمكننا إنشاء جداولنا الخاصة لاحتواء البيانات وفقًا لذلك. يُشار إلى هذه الهيكلة باسم مخطط.

أندرويد SQLite SQLiteOpenHelper

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

  1. عند تشغيل التطبيق للمرة الأولى – في هذه النقطة، ليس لدينا قاعدة بيانات بعد. لذا سنحتاج إلى إنشاء الجداول، الفهارس، البيانات الأولية، وما إلى ذلك.
  2. عند ترقية التطبيق إلى مخطط أحدث – ستظل قاعدة البيانات لدينا على المخطط القديم من الإصدار السابق للتطبيق. سيكون لدينا خيار لتغيير مخطط قاعدة البيانات لتتوافق مع احتياجات بقية التطبيق.

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

  1. البناء : يأخذ هذا البناء سياقًا (على سبيل المثال، نشاطًا)، اسم قاعدة البيانات، مصنع مؤشر اختياري (سنناقش هذا لاحقًا)، وعددًا صحيحًا يمثل إصدار مخطط قاعدة البيانات الذي تستخدمه (عادةً تبدأ من 1 وتزداد لاحقًا).

    public DatabaseHelper(Context context) {
            super(context, DB_NAME, null, DB_VERSION);
        }
    
  2. onCreate(SQLiteDatabase db): يتم استدعاؤه عندما لا تكون هناك قاعدة بيانات ويحتاج التطبيق إلى واحدة. يمرر لنا كائنًا SQLiteDatabase، يشير إلى قاعدة بيانات تم إنشاؤها حديثًا، يمكننا ملءها بالجداول والبيانات الأولية.

  3. onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion): يتم استدعاؤه عندما لا يتطابق إصدار المخطط الذي نحتاجه مع إصدار مخطط قاعدة البيانات، يمرر لنا كائنًا SQLiteDatabase وأرقام الإصدار القديم والجديد. بالتالي، يمكننا التعرف على أفضل طريقة لتحويل قاعدة البيانات من المخطط القديم إلى الجديد.

نحدد فئة DBManager لأداء جميع عمليات قاعدة البيانات CRUD (إنشاء، قراءة، تحديث، وحذف).

فتح وإغلاق اتصال قاعدة بيانات SQLite في Android

قبل أداء أي عمليات قاعدة بيانات مثل إدراج وتحديث وحذف السجلات في جدول، يتم فتح اتصال قاعدة البيانات أولاً عن طريق استدعاء الطريقة getWritableDatabase() كما هو موضح أدناه:

public DBManager open() throws SQLException {
        dbHelper = new DatabaseHelper(context);
        database = dbHelper.getWritableDatabase();
        return this;
    }

dbHelper هو مثال على الفئة الفرعية لـ SQLiteOpenHelper. لإغلاق اتصال قاعدة البيانات، يتم استدعاء الطريقة التالية.

 public void close() {
        dbHelper.close();
    }

إدراج سجل جديد في جدول قاعدة بيانات SQLite في Android

يوضح مقتطف الشفرة التالي كيفية إدراج سجل جديد في قاعدة بيانات SQLite في Android.

public void insert(String name, String desc) {
        ContentValues contentValue = new ContentValues();
        contentValue.put(DatabaseHelper.SUBJECT, name);
        contentValue.put(DatabaseHelper.DESC, desc);
        database.insert(DatabaseHelper.TABLE_NAME, null, contentValue);
    }

Content Values ينشئ مجموعة فارغة من القيم باستخدام الحجم الابتدائي المعطى. سنناقش القيم الأخرى عندما نقوم بالانتقال إلى الجزء البرمجي.

تحديث السجل في جدول قاعدة بيانات SQLite في Android

الكود التالي يوضح كيفية تحديث سجل واحد.

public int update(long _id, String name, String desc) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(DatabaseHelper.SUBJECT, name);
        contentValues.put(DatabaseHelper.DESC, desc);
        int i = database.update(DatabaseHelper.TABLE_NAME, contentValues, DatabaseHelper._ID + " = " + _id, null);
        return i;
    }

Android SQLite – حذف سجل

نحن بحاجة فقط إلى تمرير معرف السجل الذي يجب حذفه كما هو موضح أدناه.

public void delete(long _id) {
        database.delete(DatabaseHelper.TABLE_NAME, DatabaseHelper._ID + "=" + _id, null);
    }

Android SQLite Cursor

A Cursor represents the entire result set of the query. Once the query is fetched a call to cursor.moveToFirst() is made. Calling moveToFirst() does two things:

  • يتيح لنا اختبار ما إذا كانت الاستعلامات قد أرجعت مجموعة فارغة (من خلال اختبار قيمة العائد)
  • ينقل المؤشر إلى النتيجة الأولى (عندما لا تكون المجموعة فارغة)

يتم استخدام الكود التالي لاسترجاع جميع السجلات:

 public Cursor fetch() {
        String[] columns = new String[] { DatabaseHelper._ID, DatabaseHelper.SUBJECT, DatabaseHelper.DESC };
        Cursor cursor = database.query(DatabaseHelper.TABLE_NAME, columns, null, null, null, null, null);
        if (cursor != null) {
            cursor.moveToFirst();
        }
        return cursor;
    }

طريقة أخرى لاستخدام Cursor هي لفها في CursorAdapter. تمامًا مثل ArrayAdapter يكيف الصفوف، يكيف CursorAdapter كائنات Cursor، مما يجعل بياناتها متاحة لـ AdapterView مثل ListView. دعونا ننتقل إلى مشروعنا الذي يستخدم SQLite لتخزين بعض البيانات المعنية.

بنية مثال مشروع Android SQLite

في هذا التطبيق نرغب في إنشاء سجلات تخزن أسماء الدول وعملاتها المقابلة في شكل ListView. نغطي جميع الميزات المُناقَشَة أعلاه.

كود مشروع Android SQLite

يتكون التطبيق من 5 فصول. نبدأ بتحديد DatabaseHelper، وهو فرع فرعي من SQLiteOpenHelper على النحو التالي: DatabaseHelper.java

package com.journaldev.sqlite;

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

public class DatabaseHelper extends SQLiteOpenHelper {

    // اسم الجدول
    public static final String TABLE_NAME = "COUNTRIES";

    // أعمدة الجدول
    public static final String _ID = "_id";
    public static final String SUBJECT = "subject";
    public static final String DESC = "description";

    // معلومات قاعدة البيانات
    static final String DB_NAME = "JOURNALDEV_COUNTRIES.DB";

    // إصدار قاعدة البيانات
    static final int DB_VERSION = 1;

    // استعلام إنشاء الجدول
    private static final String CREATE_TABLE = "create table " + TABLE_NAME + "(" + _ID
            + " INTEGER PRIMARY KEY AUTOINCREMENT, " + SUBJECT + " TEXT NOT NULL, " + DESC + " TEXT);";

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

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }
}

كما تم مناقشته أعلاه، لقد قمنا بتجاوز الطرق onCreate() و onUpgrade() بالإضافة إلى المُنشئ. لقد قمنا بتعيين الأسماء لقاعدة البيانات والجدول على التوالي كما يلي: JOURNALDEV_COUNTRIES.DB و COUNTRIES. يتم زيادة ترقيم العمود تلقائيًا عند إدراج صف جديد. أسماء الأعمدة للبلد والعملة هي “subject” و “description”. فصول DBManager هي حيث يتم تهيئة DatabaseHelper وتعريف عمليات CRUD. فيما يلي الشيفرة لهذا الفصل: DBManager.java

package com.journaldev.sqlite;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;

public class DBManager {

    private DatabaseHelper dbHelper;

    private Context context;

    private SQLiteDatabase database;

    public DBManager(Context c) {
        context = c;
    }

    public DBManager open() throws SQLException {
        dbHelper = new DatabaseHelper(context);
        database = dbHelper.getWritableDatabase();
        return this;
    }

    public void close() {
        dbHelper.close();
    }

    public void insert(String name, String desc) {
        ContentValues contentValue = new ContentValues();
        contentValue.put(DatabaseHelper.SUBJECT, name);
        contentValue.put(DatabaseHelper.DESC, desc);
        database.insert(DatabaseHelper.TABLE_NAME, null, contentValue);
    }

    public Cursor fetch() {
        String[] columns = new String[] { DatabaseHelper._ID, DatabaseHelper.SUBJECT, DatabaseHelper.DESC };
        Cursor cursor = database.query(DatabaseHelper.TABLE_NAME, columns, null, null, null, null, null);
        if (cursor != null) {
            cursor.moveToFirst();
        }
        return cursor;
    }

    public int update(long _id, String name, String desc) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(DatabaseHelper.SUBJECT, name);
        contentValues.put(DatabaseHelper.DESC, desc);
        int i = database.update(DatabaseHelper.TABLE_NAME, contentValues, DatabaseHelper._ID + " = " + _id, null);
        return i;
    }

    public void delete(long _id) {
        database.delete(DatabaseHelper.TABLE_NAME, DatabaseHelper._ID + "=" + _id, null);
    }

}

فئة CountryListActivity.java هي النشاط الذي يتم تشغيله عند بدء التطبيق. أدناه هو تخطيط محدد لذلك: fragment_emp_list.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:dividerHeight="1dp"
        android:padding="10dp" >
    </ListView>

    <TextView
        android:id="@+id/empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/empty_list_text" />

</RelativeLayout>

هنا يتم تعريف عنصر ListView لتضمين السجلات المخزنة في قاعدة البيانات. في البداية ، ستكون قائمة العرض فارغة ، وبالتالي يتم استخدام TextView لعرض نفسه. CountryListActivity.java

package com.journaldev.sqlite;

import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;

public class CountryListActivity extends ActionBarActivity {

    private DBManager dbManager;

    private ListView listView;

    private SimpleCursorAdapter adapter;

    final String[] from = new String[] { DatabaseHelper._ID,
            DatabaseHelper.SUBJECT, DatabaseHelper.DESC };

    final int[] to = new int[] { R.id.id, R.id.title, R.id.desc };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.fragment_emp_list);

        dbManager = new DBManager(this);
        dbManager.open();
        Cursor cursor = dbManager.fetch();

        listView = (ListView) findViewById(R.id.list_view);
        listView.setEmptyView(findViewById(R.id.empty));

        adapter = new SimpleCursorAdapter(this, R.layout.activity_view_record, cursor, from, to, 0);
        adapter.notifyDataSetChanged();

        listView.setAdapter(adapter);

        // OnCLickListiner For List Items
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long viewId) {
                TextView idTextView = (TextView) view.findViewById(R.id.id);
                TextView titleTextView = (TextView) view.findViewById(R.id.title);
                TextView descTextView = (TextView) view.findViewById(R.id.desc);

                String id = idTextView.getText().toString();
                String title = titleTextView.getText().toString();
                String desc = descTextView.getText().toString();

                Intent modify_intent = new Intent(getApplicationContext(), ModifyCountryActivity.class);
                modify_intent.putExtra("title", title);
                modify_intent.putExtra("desc", desc);
                modify_intent.putExtra("id", id);

                startActivity(modify_intent);
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();
        if (id == R.id.add_record) {

            Intent add_mem = new Intent(this, AddCountryActivity.class);
            startActivity(add_mem);

        }
        return super.onOptionsItemSelected(item);
    }

}

في هذا النشاط ، يتم استدعاء كائن DBManager لأداء عمليات CRUD. يتم تعريف SimpleCursorAdapter لإضافة عناصر إلى القائمة من نتائج الاستعلام التي يتم إرجاعها في كائن Cursor. عند النقر فوق عنصر القائمة ، يتم تنفيذ نية لفتح فئة ModifyCountryActivity. يحتوي القائمة على عنصر لإضافة سجل جديد من ActionBar. هنا يتم تنفيذ نية أخرى لفتح فئة AddCountryActivity. أدناه هو الشفرة الموجودة في menu.xml. menu.xml

<menu xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    tools:context="com.example.sqlitesample.MainActivity" >

    <item
        android:id="@+id/add_record"
        android:icon="@android:drawable/ic_menu_add"
        android:orderInCategory="100"
        android:title="@string/add_record"
        app:showAsAction="always"/>

</menu>

تم تعريف تخطيط xml والشفرة الخاصة بملف AddCountryActivity.java أدناه: activity_add_record.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="20dp" >

    <EditText
        android:id="@+id/subject_edittext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="@string/enter_title" >

        <requestFocus />
    </EditText>

    <EditText
        android:id="@+id/description_edittext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="@string/enter_desc"
        android:inputType="textMultiLine"
        android:minLines="5" >
    </EditText>

    <Button
        android:id="@+id/add_record"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/add_record" />

</LinearLayout>

تم تعريف عنصري EditText التي تأخذ إدخالات الدولة والعملة جنبًا إلى جنب مع زر لإضافة القيم إلى قاعدة البيانات وعرضها في قائمة العرض. AddCountryActivity.java

package com.journaldev.sqlite;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

public class AddCountryActivity extends Activity implements OnClickListener {

    private Button addTodoBtn;
    private EditText subjectEditText;
    private EditText descEditText;

    private DBManager dbManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setTitle("Add Record");

        setContentView(R.layout.activity_add_record);

        subjectEditText = (EditText) findViewById(R.id.subject_edittext);
        descEditText = (EditText) findViewById(R.id.description_edittext);

        addTodoBtn = (Button) findViewById(R.id.add_record);

        dbManager = new DBManager(this);
        dbManager.open();
        addTodoBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.add_record:

                final String name = subjectEditText.getText().toString();
                final String desc = descEditText.getText().toString();

                dbManager.insert(name, desc);

                Intent main = new Intent(AddCountryActivity.this, CountryListActivity.class)
                        .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

                startActivity(main);
                break;
        }
    }

}

العملية CRUD التي يتم تنفيذها هنا هي إضافة سجل جديد إلى قاعدة البيانات. تم تعريف تخطيط xml وشفرة ملف ModifyCountryActivity.java أدناه: activity_modify_record.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp" >

    <EditText
        android:id="@+id/subject_edittext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:ems="10"
        android:hint="@string/enter_title" />

    <EditText
        android:id="@+id/description_edittext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="@string/enter_desc"
        android:inputType="textMultiLine"
        android:minLines="5" >
    </EditText>


    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:weightSum="2"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btn_update"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/btn_update" />

        <Button
            android:id="@+id/btn_delete"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/btn_delete" />
    </LinearLayout>

</LinearLayout>

إنه مشابه للتخطيط السابق باستثناء إضافة أزرار تعديل وحذف. ModifyCountryActivity.java

package com.journaldev.sqlite;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

public class ModifyCountryActivity extends Activity implements OnClickListener {

    private EditText titleText;
    private Button updateBtn, deleteBtn;
    private EditText descText;

    private long _id;

    private DBManager dbManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setTitle("Modify Record");

        setContentView(R.layout.activity_modify_record);

        dbManager = new DBManager(this);
        dbManager.open();

        titleText = (EditText) findViewById(R.id.subject_edittext);
        descText = (EditText) findViewById(R.id.description_edittext);

        updateBtn = (Button) findViewById(R.id.btn_update);
        deleteBtn = (Button) findViewById(R.id.btn_delete);

        Intent intent = getIntent();
        String id = intent.getStringExtra("id");
        String name = intent.getStringExtra("title");
        String desc = intent.getStringExtra("desc");

        _id = Long.parseLong(id);

        titleText.setText(name);
        descText.setText(desc);

        updateBtn.setOnClickListener(this);
        deleteBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_update:
                String title = titleText.getText().toString();
                String desc = descText.getText().toString();

                dbManager.update(_id, title, desc);
                this.returnHome();
                break;

            case R.id.btn_delete:
                dbManager.delete(_id);
                this.returnHome();
                break;
        }
    }

    public void returnHome() {
        Intent home_intent = new Intent(getApplicationContext(), CountryListActivity.class)
                .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(home_intent);
    }
}

عمليات CRUD التي يتم تنفيذها هنا هي تحديث وحذف سجل. الصور أدناه هي لقطات الشاشة للإخراج النهائي لمشروعنا. الصورة الأولى هي الناتج المرئي عند تشغيل التطبيق لأول مرة. الصورة الثانية هي نتيجة النقر فوق خيار القائمة من ActionBar لإضافة سجل جديد كما هو موضح أدناه. الصورة الثالثة تظهر الإخراج عند إضافة 3 سجلات: الصورة الرابعة تظهر الإخراج عند النقر فوق عنصر القائمة لتعديل أو حذف سجل: الصورة النهائية هي الناتج عند حذف سجل. في هذا المثال نقوم بحذف السجل الأول:

فتح ملف قاعدة بيانات Android SQLite

كما تم مناقشته سابقًا في هذا البرنامج التعليمي، يتم تخزين ملف قاعدة البيانات في التخزين الداخلي الذي يمكن الوصول إليه من مراقب جهاز Android كما هو موضح في الصورة أدناه. لعرض هذه القاعدة من البيانات، نحتاج إلى سحب هذا الملف من الجهاز إلى سطح المكتب الخاص بنا. يتم ذلك عن طريق النقر فوق خيار القائمة في الزاوية العلوية اليمنى كما هو موضح في الصورة أدناه: لفتح هذا الملف، قم بتنزيل SQLiteBrowser من هذا الرابط. تُظهر المقاطع الموجودة أدناه الهيكل والجداول في المتصفح. لعرض الجدول، انتقل إلى علامة تبويب تصفح البيانات في الأعلى. يمكن رؤية الصورة التالية: هذا يضع نهاية لبرنامج تعليمي حول SQLite لنظام Android. يمكن تنزيل المشروع النهائي مشروع Android SQLite من الرابط أدناه.

تنزيل مشروع مثال Android SQLite

Source:
https://www.digitalocean.com/community/tutorials/android-sqlite-database-example-tutorial