Bagaimana cara menggunakan pernyataan yang disiapkan di SQlite di Android?

100

Bagaimana cara menggunakan pernyataan yang disiapkan di SQlite di Android?

pupeno
sumber
9
Pertimbangkan untuk mengubah jawaban yang diterima.
Suragch
9
setuju - pertimbangkan untuk mengubah jawaban ... mentalitas kelompok mendukung jawaban yang diterima, ketika ada solusi yang lebih baik.
goodguys_activate
4
Pablo, tolong ubah jawaban yang diterima ... contoh yang diberikan bahkan tidak berjalan. Dan suara negatif kami tidak cukup untuk melepaskannya.
SparK

Jawaban:

21

Saya menggunakan pernyataan yang sudah disiapkan di Android sepanjang waktu, ini cukup sederhana:

SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
jasonhudgins
sumber
81
Saya tidak tahu bagaimana jawaban ini bisa mendapatkan banyak suara. SQLIteStatement # mengeksekusi tidak boleh digunakan untuk kueri Sql, hanya pernyataan. Silakan periksa developer.android.com/reference/android/database/sqlite/…
simao
9
Lalu bagaimana Anda seharusnya menggunakan pernyataan yang disiapkan untuk menanyakan data?
Juan Mendes
12
Perhatikan bahwa SQLiteStatement.bindXXX()memiliki indeks berbasis 1, bukan berbasis 0 seperti yang lainnya.
Simulant
6
@jasonhudgins Mengapa tidak hanya mengganti SELECT Anda dengan INSERT? Saya baru saja keluar dari utas ini , di mana Anda telah membingungkan pemula
keyer
35
contoh sempurna dari mentalitas kelompok yang memberi suara positif dan menerima jawaban yang sepenuhnya salah
165

Untuk pernyataan SQLite yang disiapkan di Android ada SQLiteStatement . Pernyataan yang telah disiapkan membantu Anda mempercepat kinerja (terutama untuk pernyataan yang perlu dijalankan beberapa kali) dan juga membantu menghindari serangan injeksi. Lihat artikel ini untuk pembahasan umum tentang pernyataan yang disiapkan.

SQLiteStatementdimaksudkan untuk digunakan dengan pernyataan SQL yang tidak mengembalikan banyak nilai. (Itu berarti Anda tidak akan menggunakannya untuk sebagian besar kueri.) Berikut adalah beberapa contohnya:

Buat tabel

String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();

The execute()metode tidak mengembalikan nilai sehingga sangat tepat untuk digunakan dengan MENCIPTAKAN dan DROP tetapi tidak dimaksudkan untuk digunakan dengan SELECT, INSERT, DELETE, dan UPDATE karena kembali nilai-nilai tersebut. (Tapi lihat pertanyaan ini .)

Sisipkan nilai

String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();

Perhatikan bahwa executeInsert()metode yang digunakan daripada execute(). Tentu saja, Anda tidak ingin selalu memasukkan hal yang sama di setiap baris. Untuk itu Anda bisa menggunakan binding .

String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
SQLiteStatement statement = db.compileStatement(sql);

int intValue = 57;
String stringValue = "hello";

statement.bindLong(1, intValue); // 1-based: matches first '?' in sql string
statement.bindString(2, stringValue);  // matches second '?' in sql string

long rowId = statement.executeInsert();

Biasanya Anda menggunakan pernyataan yang sudah disiapkan ketika Anda ingin dengan cepat mengulangi sesuatu (seperti INSERT) berkali-kali. Pernyataan yang disiapkan membuatnya sehingga pernyataan SQL tidak harus diurai dan dikompilasi setiap saat. Anda bahkan dapat mempercepat lebih banyak dengan menggunakan transaksi . Ini memungkinkan semua perubahan diterapkan sekaligus. Berikut ini contohnya:

String stringValue = "hello";
try {

    db.beginTransaction();
    String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
    SQLiteStatement statement = db.compileStatement(sql);

    for (int i = 0; i < 1000; i++) {
        statement.clearBindings();
        statement.bindLong(1, i);
        statement.bindString(2, stringValue + i);
        statement.executeInsert();
    }

    db.setTransactionSuccessful(); // This commits the transaction if there were no exceptions

} catch (Exception e) {
    Log.w("Exception:", e);
} finally {
    db.endTransaction();
}

Lihat tautan ini untuk beberapa info bagus lainnya tentang transaksi dan mempercepat penyisipan database.

Perbarui baris

Ini adalah contoh dasar. Anda juga dapat menerapkan konsep dari bagian di atas.

String sql = "UPDATE table_name SET column_2=? WHERE column_1=?";
SQLiteStatement statement = db.compileStatement(sql);

int id = 7;
String stringValue = "hi there";

statement.bindString(1, stringValue);
statement.bindLong(2, id);

int numberOfRowsAffected = statement.executeUpdateDelete();

Hapus baris

The executeUpdateDelete()Metode ini juga dapat digunakan untuk laporan DELETE dan diperkenalkan pada API 11. Lihat ini Q & A .

Berikut ini contohnya.

try {

    db.beginTransaction();
    String sql = "DELETE FROM " + table_name +
            " WHERE " + column_1 + " = ?";
    SQLiteStatement statement = db.compileStatement(sql);

    for (Long id : words) {
        statement.clearBindings();
        statement.bindLong(1, id);
        statement.executeUpdateDelete();
    }

    db.setTransactionSuccessful();

} catch (SQLException e) {
    Log.w("Exception:", e);
} finally {
    db.endTransaction();
}

Pertanyaan

Biasanya saat Anda menjalankan kueri, Anda ingin mendapatkan kursor kembali dengan banyak baris. Itu bukan untuk apa SQLiteStatement. Anda tidak menjalankan kueri dengannya kecuali Anda hanya memerlukan hasil yang sederhana, seperti jumlah baris dalam database, yang dapat Anda lakukan dengansimpleQueryForLong()

String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();

Biasanya Anda akan menjalankan query()metode SQLiteDatabase untuk mendapatkan kursor.

SQLiteDatabase db = dbHelper.getReadableDatabase();
String table = "table_name";
String[] columnsToReturn = { "column_1", "column_2" };
String selection = "column_1 =?";
String[] selectionArgs = { someValue }; // matched to "?" in selection
Cursor dbCursor = db.query(table, columnsToReturn, selection, selectionArgs, null, null, null);

Lihat jawaban ini untuk detail yang lebih baik tentang kueri.

Suragch
sumber
5
Sekadar pengingat: metode .bindString / .bindLong / ... semuanya berbasis 1.
Denys Vitali
Saya mencari di balik terpal metode kenyamanan Android seperti .query, .insert dan .delete dan memperhatikan bahwa mereka menggunakan SQLiteStatement di bawah tenda. Bukankah lebih mudah menggunakan metode praktis daripada membuat pernyataan Anda sendiri?
Nicolás Carrasco
@ NicolásCarrasco, sudah lama sejak saya mengerjakan ini jadi saya sedikit berkarat sekarang. Untuk kueri dan sisipan tunggal, pembaruan dan penghapusan, saya pasti akan menggunakan metode kenyamanan. Namun, jika Anda melakukan penyisipan massal, pembaruan, atau penghapusan, saya akan mempertimbangkan pernyataan yang disiapkan bersama dengan transaksi. Mengenai SQLiteStatement yang digunakan di bawah tenda dan apakah metode kenyamanannya cukup baik, saya tidak dapat membicarakannya. Saya kira saya akan mengatakan, jika metode kenyamanan bekerja cukup cepat untuk Anda, maka gunakanlah.
Suragch
Mengapa Anda menggunakan clearBindings ()? Anda mengikat semua nilai Anda tanpa syarat apapun. Tidak masuk akal bagi saya untuk menetapkannya nol terlebih dahulu dan ke nilai sebenarnya.
Jan
@TheincredibleJan, saya tidak tahu pasti. Ini mungkin tidak perlu dan Anda dapat mencobanya tanpa menghapus binding untuk melihat apakah ada bedanya. Namun demikian, panggilan clearBindings()tidak hanya mengaturnya ke null(lihat kode sumber ). Saya melihatnya sebagai membersihkan status sehingga tidak ada yang mempengaruhinya dari loop sebelumnya. Mungkin itu tidak perlu. Saya akan senang untuk seseorang yang tahu untuk berkomentar.
Suragch
22

Jika Anda ingin kursor kembali, Anda dapat mempertimbangkan sesuatu seperti ini:

SQLiteDatabase db = dbHelper.getWritableDatabase();

public Cursor fetchByCountryCode(String strCountryCode)
{
    /**
     * SELECT * FROM Country
     *      WHERE code = US
     */
    return cursor = db.query(true, 
        "Country",                        /**< Table name. */
        null,                             /**< All the fields that you want the 
                                                cursor to contain; null means all.*/
        "code=?",                         /**< WHERE statement without the WHERE clause. */
        new String[] { strCountryCode },    /**< Selection arguments. */
        null, null, null, null);
}

/** Fill a cursor with the results. */
Cursor c = fetchByCountryCode("US");

/** Retrieve data from the fields. */
String strCountryCode = c.getString(cursor.getColumnIndex("code"));

/** Assuming that you have a field/column with the name "country_name" */
String strCountryName = c.getString(cursor.getColumnIndex("country_name"));

Lihat cuplikan Genskrip ini jika Anda ingin yang lebih lengkap. Perhatikan bahwa ini adalah kueri SQL berparameter, jadi pada dasarnya, ini adalah pernyataan yang disiapkan.

jbaez.dll
sumber
Kesalahan kecil pada kode di atas: Seharusnya "String baru [] {strCountryCode}", bukan "String baru {strCountryCode}".
Pierre-Luc Simard
Anda perlu memindahkan kursor sebelum Anda dapat mengambil data
Chin
9

Contoh jasonhudgins tidak akan bekerja. Anda tidak dapat menjalankan kueri dengan stmt.execute()dan mendapatkan nilai (atau a Cursor) kembali.

Anda hanya dapat mengkompilasi pernyataan yang tidak mengembalikan baris sama sekali (seperti menyisipkan, atau membuat pernyataan tabel) atau satu baris dan kolom, (dan menggunakan simpleQueryForLong()atau simpleQueryForString()).

redfish64
sumber
1

Untuk mendapatkan kursor, Anda tidak bisa menggunakan compiledStatement. Namun, jika Anda ingin menggunakan pernyataan SQL yang disiapkan penuh, saya merekomendasikan adaptasi metode jbaez ... Menggunakan db.rawQuery()alih-alih db.query().

Aaron
sumber