SQLite ব্যবহার করে ডেটা সংরক্ষণ করুন

একটি ডাটাবেসে ডেটা সংরক্ষণ করা পুনরাবৃত্তি বা কাঠামোগত ডেটার জন্য আদর্শ, যেমন যোগাযোগের তথ্য। এই পৃষ্ঠাটি অনুমান করে যে আপনি সাধারণভাবে SQL ডাটাবেসগুলির সাথে পরিচিত এবং আপনাকে Android এ SQLite ডেটাবেসগুলির সাথে শুরু করতে সহায়তা করে৷ অ্যান্ড্রয়েডে একটি ডাটাবেস ব্যবহার করার জন্য আপনার যে APIগুলি প্রয়োজন হবে সেগুলি android.database.sqlite প্যাকেজে উপলব্ধ৷

সতর্কতা: যদিও এই API গুলি শক্তিশালী, তারা মোটামুটি নিম্ন-স্তরের এবং ব্যবহার করার জন্য প্রচুর সময় এবং প্রচেষ্টার প্রয়োজন:

  • কাঁচা SQL প্রশ্নের কোন কম্পাইল-টাইম যাচাইকরণ নেই। আপনার ডেটা গ্রাফ পরিবর্তিত হওয়ার সাথে সাথে আপনাকে প্রভাবিত এসকিউএল কোয়েরিগুলি ম্যানুয়ালি আপডেট করতে হবে। এই প্রক্রিয়া সময়সাপেক্ষ এবং ত্রুটি প্রবণ হতে পারে.
  • SQL কোয়েরি এবং ডেটা অবজেক্টের মধ্যে রূপান্তর করতে আপনাকে প্রচুর বয়লারপ্লেট কোড ব্যবহার করতে হবে।

এই কারণে, আমরা আপনার অ্যাপের SQLite ডেটাবেসে তথ্য অ্যাক্সেস করার জন্য রুম পারসিস্টেন্স লাইব্রেরিটিকে একটি বিমূর্ত স্তর হিসাবে ব্যবহার করার পরামর্শ দিই

একটি স্কিমা এবং চুক্তি সংজ্ঞায়িত করুন

এসকিউএল ডাটাবেসের অন্যতম প্রধান নীতি হল স্কিমা: ডাটাবেস কীভাবে সংগঠিত হয় তার একটি আনুষ্ঠানিক ঘোষণা। স্কিমাটি SQL স্টেটমেন্টে প্রতিফলিত হয় যা আপনি আপনার ডাটাবেস তৈরি করতে ব্যবহার করেন। আপনি একটি সহচর শ্রেণী তৈরি করতে সহায়ক বলে মনে করতে পারেন, যা একটি চুক্তি শ্রেণী হিসাবে পরিচিত, যা আপনার স্কিমার বিন্যাসটি একটি পদ্ধতিগত এবং স্ব-নথিপত্রের উপায়ে স্পষ্টভাবে নির্দিষ্ট করে৷

একটি চুক্তি শ্রেণী হল ধ্রুবকগুলির জন্য একটি ধারক যা URI, টেবিল এবং কলামগুলির নাম নির্ধারণ করে। কন্ট্রাক্ট ক্লাস আপনাকে একই প্যাকেজের অন্যান্য সমস্ত ক্লাসে একই ধ্রুবক ব্যবহার করতে দেয়। এটি আপনাকে এক জায়গায় একটি কলামের নাম পরিবর্তন করতে দেয় এবং এটি আপনার কোড জুড়ে প্রচার করতে দেয়।

একটি কন্ট্রাক্ট ক্লাস সংগঠিত করার একটি ভাল উপায় হল ক্লাসের রুট লেভেলে আপনার পুরো ডাটাবেসে বিশ্বব্যাপী সংজ্ঞাগুলি রাখা। তারপর প্রতিটি টেবিলের জন্য একটি অভ্যন্তরীণ ক্লাস তৈরি করুন। প্রতিটি অভ্যন্তরীণ শ্রেণী সংশ্লিষ্ট টেবিলের কলাম গণনা করে।

দ্রষ্টব্য: BaseColumns ইন্টারফেস প্রয়োগ করে, আপনার অভ্যন্তরীণ শ্রেণীটি _ID নামক একটি প্রাথমিক কী ক্ষেত্র উত্তরাধিকার সূত্রে পেতে পারে যা কিছু অ্যান্ড্রয়েড ক্লাস যেমন CursorAdapter আশা করে। এটির প্রয়োজন নেই, তবে এটি আপনার ডেটাবেসকে Android ফ্রেমওয়ার্কের সাথে সামঞ্জস্যপূর্ণভাবে কাজ করতে সহায়তা করতে পারে।

উদাহরণস্বরূপ, নিম্নলিখিত চুক্তিটি একটি 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";
    }
}

একটি এসকিউএল হেল্পার ব্যবহার করে একটি ডাটাবেস তৈরি করুন

একবার আপনি আপনার ডাটাবেস কেমন দেখায় তা সংজ্ঞায়িত করলে, আপনাকে এমন পদ্ধতি প্রয়োগ করতে হবে যা ডাটাবেস এবং টেবিল তৈরি এবং বজায় রাখে। এখানে কিছু সাধারণ বিবৃতি রয়েছে যা একটি টেবিল তৈরি এবং মুছে দেয়:

কোটলিন

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() কল করেছেন৷ আরও তথ্যের জন্য অ্যান্ড্রয়েডে থ্রেডিং দেখুন।

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());

একটি ডাটাবেসে তথ্য রাখুন

insert() পদ্ধতিতে একটি ContentValues ​​অবজেক্ট পাস করে ডাটাবেসে ডেটা সন্নিবেশ করুন:

কোটলিন

// 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 উল্লেখ করেন, যেমন এই কোড নমুনাতে, কোনো মান না থাকলে কাঠামোটি একটি সারি সন্নিবেশ করে না।

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 ক্লজ তৈরি করতে একত্রিত হয়। কারণ আর্গুমেন্টগুলি নির্বাচন ক্যোয়ারী থেকে আলাদাভাবে প্রদান করা হয়েছে, সেগুলি একত্রিত হওয়ার আগে এড়িয়ে গেছে৷ এটি আপনার নির্বাচনের বিবৃতিগুলিকে এসকিউএল ইনজেকশন থেকে প্রতিরোধী করে তোলে। সমস্ত আর্গুমেন্ট সম্পর্কে আরো বিস্তারিত জানার জন্য, query() রেফারেন্স দেখুন।

কার্সারে একটি সারি দেখতে, Cursor সরানোর পদ্ধতিগুলির মধ্যে একটি ব্যবহার করুন, যা আপনাকে অবশ্যই মান পড়া শুরু করার আগে অবশ্যই কল করতে হবে। যেহেতু কার্সার পজিশন -1 থেকে শুরু হয়, তাই moveToNext() কল করলে ফলাফলের প্রথম এন্ট্রিতে "রিড পজিশন" থাকে এবং ফলাফল সেটে কার্সারটি ইতিমধ্যেই শেষ এন্ট্রি অতিক্রম করেছে কিনা তা ফেরত দেয়। প্রতিটি সারির জন্য, আপনি একটি কলামের মান পড়তে পারেন একটি Cursor প্রাপ্ত পদ্ধতি, যেমন getString() বা getLong() কল করে। প্রতিটি প্রাপ্ত পদ্ধতির জন্য, আপনাকে অবশ্যই আপনার পছন্দসই কলামের সূচক অবস্থানটি পাস করতে হবে, যা আপনি 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() পদ্ধতিতে নির্বাচন আর্গুমেন্টের মতোই কাজ করে। এটি নির্বাচন স্পেসিফিকেশনকে একটি নির্বাচন ধারা এবং নির্বাচন আর্গুমেন্টে ভাগ করে। ধারাটি দেখার জন্য কলামগুলিকে সংজ্ঞায়িত করে এবং আপনাকে কলাম পরীক্ষাগুলিকে একত্রিত করার অনুমতি দেয়। আর্গুমেন্ট হল মান যেগুলির বিরুদ্ধে পরীক্ষা করার জন্য ধারার মধ্যে আবদ্ধ। কারণ ফলাফলটি নিয়মিত এসকিউএল স্টেটমেন্টের মতো পরিচালনা করা হয় না, এটি এসকিউএল ইনজেকশন থেকে প্রতিরোধী।

কোটলিন

// 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() পদ্ধতিটি ব্যবহার করুন।

সারণি আপডেট করা হলে insert() -এর ContentValues ​​সিনট্যাক্স-এর সাথে 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() এ ডাটাবেস বন্ধ করা সর্বোত্তম।

কোটলিন

override fun onDestroy() {
    dbHelper.close()
    super.onDestroy()
}

জাভা

@Override
protected void onDestroy() {
    dbHelper.close();
    super.onDestroy();
}

আপনার ডাটাবেস ডিবাগ করুন

Android SDK-এ একটি sqlite3 শেল টুল রয়েছে যা আপনাকে টেবিলের বিষয়বস্তু ব্রাউজ করতে, SQL কমান্ড চালাতে এবং SQLite ডাটাবেসে অন্যান্য দরকারী ফাংশন সম্পাদন করতে দেয়। আরও তথ্যের জন্য, কিভাবে শেল কমান্ড ইস্যু করতে হয় তা দেখুন।