Menyimpan Data di Database SQL

Menyimpan data ke suatu database adalah hal yang ideal untuk data yang terstruktur atau data yang berulang, misalnya informasi kontak. Latihan ini mengasumsikan bahwa kita familiar dengan database-database SQL secara umum dan membantu kita mulai dengan database SQLite di Android. API-API yang akan kita perlukan untuk memanfaatkan database di Android ada di package 'android.database.sqlite'.


Membuat 'Schema' dan 'Contract'

Salah satu prinsip utama dari database SQL adalah 'schema': yaitu suatu deklarasi/pernyataan formal tentang bagaimana database akan diatur. 'Schema' akan tercermin di dalam statemen-statemen SQL yang kita gunakan untuk membuat database. Kita mungkin merasa perlu untuk membuat class pendamping, yang disebut dengan class 'contract', yang secara eksplisit menetapkan layout 'schema' kita dengan cara yang sistematis.

Suatu class 'contract' adalah wadah untuk konstanta-konstanta yang mendefinisikan nama URI, tabel, dan kolom. Class 'contract' mengijinkan kita untuk menggunakan konstanta yang sama di semua class-class lainnya di dalam 'package' yang sama. Ini akan membolehkan kita untuk mengubah nama kolom di satu tempat dan menyebarkannya ke seluruh kode kita.

Suatu cara yang bagus dalam mengorganisir class 'contract' adalah dengan menempatkan definisi-definisi yang global ke seluruh database kita di level 'root' dari class tersebut. Kemudian membuat 'inner class' untuk masing-masing tabel yang akan meng-enumerasi kolom-kolomnya.
Catatan: 
Dengan mengimplementasikan interface 'BaseColumns', 'inner class' kita bisa mewarisi kolom 'primary key' yang disebut '_ID' dimana beberapa class Android seperti 'cursor adaptors' menginginkannya. Ini tidak diperlukan, tetapi ini bisa membantu database kita bekerja secara harmonis dengan framework Android.
Contohnya, 'snippet' berikut di bawah ini akan mendefinisikan nama tabel dan nama-nama kolom untuk satu tabel:

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";
    }
}

Membuat Database dengan Menggunakan 'SQL Helper'

Setelah kita mendefinisikan database kita akan seperti apa, kita seharusnya mengimplementasikan method-method yang membuat dan me-maintain database dan tabel-tabelnya. Berikut dibawah ini adalah beberapa statemen yang biasa digunakan untuk membuat dan menghapus tabel:

private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    FeedEntry.COLUMN_NAME_SUBTITLE + TEXT_TYPE + " )";
private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
Persis seperti file-file yang kita simpan di storage 'internal', Android akan menyimpan database kita di ruang disk 'private' yang terasosiasi dengan aplikasi. Data kita akan aman, karena secara default area ini tidak bisa diakses oleh aplikasi lainnya.

Kumpulan API-API yang sangat berguna untuk melakukan ini semua ada di class 'SQLiteOpenHelper'. Ketika kita menggunakan class ini untuk mendapatkan acuan ke database kita, sistem akan melakukan pekerjaan yang berpotensi berjalan sangat lama (lambat) ketika membuat dan meng-update database hanya ketika pada saat diperlukan dan bukan pada saat app mulai di awal. Yang perlu kita lakukan adalah memanggil 'getWritableDatabase()' atau 'getReadableDatabase()'.
Catatan:
Karena pekerjaan-pekerjaan ke database bisa berjalan lama (lambat), maka pastikan bahwa kita memanggil method-method 'getWritableDatabase()' atau 'getReadableDatabase()' di 'background', misalnya dengan 'AsyncTask' atau 'IntentService'.
Untuk menggunakan 'SQLiteOpenHelper', kita buat subclass yang menimpa (override) method-method 'onCreate()', 'onUpgrade()' dan 'onOpen()'. Kita mungkin juga ingin mengimplementasikan 'onDownGrade()', tetapi ini tidak diperlukan.

Contohnya, berikut di bawah ini adalah implementasi dari 'SQLiteOpenHelper' yang menggunakan beberapa perintah yang ditunjukkan di atas:

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

Untuk mengakses database kita, kita perlu membuat instan (object) subclass dari 'SQLiteOpenHelper' kita:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

Menyimpan Informasi ke Database

Memasukkan data ke database (insert) dengan cara melewatkan object 'ContentValues' ke method 'insert()':

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.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);
Argumen (parameter) pertama untuk 'insert()' adalah nama tabel-nya.

Argumen (parameter) kedua akan memberitahu framework apa yang harus dilakukan ketika 'ContentValues' adalah kosong (misalnya, kita tidak menyimpan nilai apapun). Bila kita menyebutkan nama kolom, framework akan menambahkan (insert) baris dan men-set nilai kolom tersebut ke null. Bila kita menetapkan null, seperti di dalam contoh kode ini, framework tidak akan menambahkan (insert) baris ketika tidak ada nilai-nilainya.


Membaca Informasi dari Database

Untuk membaca dari database, kita menggunakan method 'query()', dengan melewatkan kriteria pemilihan kita dan kolom-kolom yang dinginkan. Method tersebut akan menggabungkan elemen-elemen 'insert()' dan 'update()', kecuali daftar kolom akan mendefiniskan data yang ingin kita ambil, dan bukan data yang akan kita masukkan/insert. Hasil-hasil dari 'query' akan dikembalikan dalam bentuk object 'Cursor'.

SQLiteDatabase db = mDbHelper.getReadableDatabase();
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedEntry._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 c = db.query(
    FeedEntry.TABLE_NAME,                     // The table to query
    projection,                               // The columns to return
    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
    );
Untuk melihat ke suatu baris di dalam 'cursor', kita gunakan salah satu method 'move' dari 'Cursor', yang harus selalu kita panggil sebelum mulai membaca nilai-nilainya. Pada umumnya, kita seharusnya mulai dengan memanggil 'moveToFirst()', yang menaruh posisi 'baca' pada 'entry' pertama pada hasilnya. Untuk masing-masing baris, kita bisa membaca suatu nilai kolom dengan memanggil salah satu method 'get' dari 'Cursor', misalnya 'getString()' atau 'getLong()'. Untuk masing-masing method 'get', kita harus melewatkan posisi index dari kolom yang kita inginkan, yang bisa kita dapatkan dengan memanggil 'getColumnIndex()' atau 'getColumnIndexOrThrow()'. Contohnya:

cursor.moveToFirst();
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedEntry._ID)
);

Menghapus Informasi dari Database

Untuk menghapus baris-baris dari suatu tabel, kita perlu memberikan kriteria pemilihan yang mengidentifikasikan baris-barisnya. API database akan memberikan mekanisme untuk membuat kriteria pemilihan yang melindungin dari 'SQL injection'. Mekanisme ini membagi spesifikasi pemilihan menjadi ketentuan pemilihan dan argumen pemilihan. Ketentuan tersebut akan mendefiniskan kolom-kolom yang akan dilihat, dan juga akan mengijinkan kita menggabungkan 'tes' kolom. Argumen-argumen adalah nilai-nilai yang di-tes terhadap ketentuan tadi. Karena hasilnya tidak ditangani sama seperti statemen SQL biasa, hal ini akan kebal terhadap 'SQL injection'.

// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { "MyTitle" };
// Issue SQL statement.
db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);

Meng-update Database

Ketika kita perlu memodifikasi bagian dari nilai-nilai database kita, kita akan menggunakan method 'update()'.

Meng-update tabel akan meng-kombinasikan sintaks 'insert()' dengan sintaks 'where' dari 'delete()'.

SQLiteDatabase db = mDbHelper.getReadableDatabase();
// New value for one column
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 = { "MyTitle" };
int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);
license: cc by

No comments: